1
Fork 0
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:
hz2 2025-05-04 09:33:09 -07:00 committed by GitHub
parent fd29eb5fc1
commit 6d29b7b3c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 82 additions and 6 deletions

View file

@ -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()
}
}
}
}

View file

@ -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) => {

View file

@ -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!());