mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 19:17:43 +00:00
Tail macos stdin ran from script fix (#7844)
* fixes #7763 - introduce macOS-specific config guard - added test for testing tail stdin when redirected (`>`) from file and when through a pipe (`|`) * created test to mock behavior in #7763, with comments added drop line * re-enabled test_stdin_redirect_dir_when_target_os_is_macos, and added a check to handle error message * added location of current directory so test env can find script * adjusting to try to have FreeBSD find the file in CI test * putting in /env instead of assuming bash * removed ignore macro * added comments explaining the need for specific macOS cases, including reference to rust-lang issue: https://github.com/rust-lang/rust/issues/95239
This commit is contained in:
parent
fd29eb5fc1
commit
6d29b7b3c1
3 changed files with 82 additions and 6 deletions
|
@ -78,14 +78,18 @@ impl Input {
|
|||
path.canonicalize().ok()
|
||||
}
|
||||
InputKind::File(_) | InputKind::Stdin => {
|
||||
if cfg!(unix) {
|
||||
match PathBuf::from(text::DEV_STDIN).canonicalize().ok() {
|
||||
Some(path) if path != PathBuf::from(text::FD0) => Some(path),
|
||||
Some(_) | None => None,
|
||||
}
|
||||
} else {
|
||||
// on macOS, /dev/fd isn't backed by /proc and canonicalize()
|
||||
// on dev/fd/0 (or /dev/stdin) will fail (NotFound),
|
||||
// so we treat stdin as a pipe here
|
||||
// https://github.com/rust-lang/rust/issues/95239
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
None
|
||||
}
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
{
|
||||
PathBuf::from(text::FD0).canonicalize().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,6 +189,29 @@ fn tail_stdin(
|
|||
input: &Input,
|
||||
observer: &mut Observer,
|
||||
) -> UResult<()> {
|
||||
// on macOS, resolve() will always return None for stdin,
|
||||
// we need to detect if stdin is a directory ourselves.
|
||||
// fstat-ing certain descriptors under /dev/fd fails with
|
||||
// bad file descriptor or might not catch directory cases
|
||||
// e.g. see the differences between running ls -l /dev/stdin /dev/fd/0
|
||||
// on macOS and Linux.
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Ok(mut stdin_handle) = Handle::stdin() {
|
||||
if let Ok(meta) = stdin_handle.as_file_mut().metadata() {
|
||||
if meta.file_type().is_dir() {
|
||||
set_exit_code(1);
|
||||
show_error!(
|
||||
"cannot open '{}' for reading: {}",
|
||||
input.display_name,
|
||||
text::NO_SUCH_FILE
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match input.resolve() {
|
||||
// fifo
|
||||
Some(path) => {
|
||||
|
|
|
@ -346,6 +346,55 @@ fn test_stdin_redirect_dir_when_target_os_is_macos() {
|
|||
.stderr_is("tail: cannot open 'standard input' for reading: No such file or directory\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_stdin_via_script_redirection_and_pipe() {
|
||||
// $ touch file.txt
|
||||
// $ echo line1 > file.txt
|
||||
// $ echo line2 >> file.txt
|
||||
// $ chmod +x test.sh
|
||||
// $ ./test.sh < file.txt
|
||||
// line1
|
||||
// line2
|
||||
// $ cat file.txt | ./test.sh
|
||||
// line1
|
||||
// line2
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let at = &scene.fixtures;
|
||||
let data = "line1\nline2\n";
|
||||
|
||||
at.write("file.txt", data);
|
||||
|
||||
let mut script = at.make_file("test.sh");
|
||||
writeln!(script, "#!/usr/bin/env sh").unwrap();
|
||||
writeln!(script, "tail").unwrap();
|
||||
script
|
||||
.set_permissions(PermissionsExt::from_mode(0o755))
|
||||
.unwrap();
|
||||
|
||||
drop(script); // close the file handle to ensure file is not busy
|
||||
|
||||
// test with redirection
|
||||
scene
|
||||
.cmd("sh")
|
||||
.current_dir(at.plus(""))
|
||||
.arg("-c")
|
||||
.arg("./test.sh < file.txt")
|
||||
.succeeds()
|
||||
.stdout_only(data);
|
||||
|
||||
// test with pipe
|
||||
scene
|
||||
.cmd("sh")
|
||||
.current_dir(at.plus(""))
|
||||
.arg("-c")
|
||||
.arg("cat file.txt | ./test.sh")
|
||||
.succeeds()
|
||||
.stdout_only(data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_follow_stdin_descriptor() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue