From e00bd341c38c760900f11179f53e3a933856878a Mon Sep 17 00:00:00 2001 From: Pablo Garcia Date: Wed, 19 Aug 2026 14:04:08 +0200 Subject: [PATCH 1/2] find: warn on -name/-path patterns with a directory separator GNU find emits a warning when a -name/-iname pattern contains a '/' (it matches basenames only, so the pattern can never match and the user likely meant -wholename) and when a -path/-wholename pattern ends with '/' (a trailing separator can never match a real path). find now emits the same warnings on stderr while still exiting successfully. A pattern made up only of '/' (e.g. `-name /` or `-path /`) is exempt: '/' is a valid basename for the root entry, so warning would be a false alarm. GNU itself miscategorises this case (bug #62227); the existing find_slashes test encodes the correct behaviour and is preserved. Closes #783 --- src/find/matchers/mod.rs | 28 +++++++++++++++++++ tests/test_find.rs | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/find/matchers/mod.rs b/src/find/matchers/mod.rs index 1a2cc68a..27e630fd 100644 --- a/src/find/matchers/mod.rs +++ b/src/find/matchers/mod.rs @@ -65,6 +65,7 @@ use ls::Ls; use std::{ error::Error, fs::{File, Metadata}, + io::{stderr, Write}, path::{Path, PathBuf}, str::FromStr, time::SystemTime, @@ -534,6 +535,20 @@ fn build_matcher_tree( return Err(From::from(format!("missing argument to {}", args[i]))); } i += 1; + // GNU find warns when a -name/-iname pattern contains a directory + // separator: -name matches basenames only, so such a pattern can + // never match and the user likely meant -wholename. See issue #783. + // A pattern made up only of '/' (e.g. `-name /`) is left alone: `/` + // is a valid basename for the root entry, so warning would be a + // false alarm (GNU itself miscategorises this — bug #62227). + if args[i].contains('/') && args[i].chars().any(|c| c != '/') { + writeln!( + &mut stderr(), + "find: warning: '{}' matches against basenames only, but the given pattern contains a directory separator ('/'), thus the expression will evaluate to false all the time. Did you mean '-wholename'?", + args[i - 1] + ) + .unwrap(); + } Some(NameMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box()) } "-path" | "-ipath" | "-wholename" | "-iwholename" => { @@ -541,6 +556,19 @@ fn build_matcher_tree( return Err(From::from(format!("missing argument to {}", args[i]))); } i += 1; + // GNU find warns when a -path/-wholename pattern ends with '/': + // a trailing separator can never match a real path. See issue #783. + // As with -name, a pattern of only '/' is exempt: `-path /` can + // legitimately match the root entry. + if args[i].ends_with('/') && args[i].chars().any(|c| c != '/') { + writeln!( + &mut stderr(), + "find: warning: {} {} will not match anything because it ends with /.", + args[i - 1], + args[i] + ) + .unwrap(); + } Some(PathMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box()) } "-readable" => Some(AccessMatcher::Readable.into_box()), diff --git a/tests/test_find.rs b/tests/test_find.rs index 26f9a6d8..8dc3f6cf 100644 --- a/tests/test_find.rs +++ b/tests/test_find.rs @@ -1614,3 +1614,63 @@ fn find_exits_cleanly_on_broken_pipe() { "find panicked instead of exiting cleanly on a broken pipe:\n{stderr}" ); } + +// GNU find emits a warning when a -name/-iname pattern contains a directory +// separator (it matches basenames only, so the pattern can never match) and +// when a -path/-wholename pattern ends with '/'. find should warn on stderr +// but still exit successfully. See issue #783. +#[test] +fn name_pattern_with_separator_warns() { + ucmd() + .args(&["-name", "a/b"]) + .succeeds() + .stderr_contains("'-name' matches against basenames only") + .stderr_contains("directory separator ('/')") + .stderr_contains("Did you mean '-wholename'?"); + + ucmd() + .args(&["-iname", "a/b"]) + .succeeds() + .stderr_contains("'-iname' matches against basenames only"); +} + +#[test] +fn path_pattern_ending_with_separator_warns() { + ucmd() + .args(&["-path", "a/"]) + .succeeds() + .stderr_contains("-path a/ will not match anything because it ends with /."); + + ucmd() + .args(&["-wholename", "a/"]) + .succeeds() + .stderr_contains("-wholename a/ will not match anything because it ends with /."); + + ucmd() + .args(&["-ipath", "a/"]) + .succeeds() + .stderr_contains("-ipath a/ will not match anything because it ends with /."); +} + +#[test] +fn name_pattern_without_separator_does_not_warn() { + ucmd().args(&["-name", "a.txt"]).succeeds().no_stderr(); + ucmd().args(&["-path", "a.txt"]).succeeds().no_stderr(); + ucmd().args(&["-wholename", "a.txt"]).succeeds().no_stderr(); +} + +// A pattern made up only of '/' (e.g. `-name /` or `-path /`) is a legitimate +// way to match the root entry, so it must NOT trigger the separator warning +// (GNU itself gets this wrong — bug #62227). Covers the existing `find_slashes` +// behavior. +#[test] +fn all_slash_pattern_does_not_warn() { + ucmd() + .args(&["///", "-maxdepth", "0", "-name", "/"]) + .succeeds() + .no_stderr(); + ucmd() + .args(&["/", "-maxdepth", "0", "-path", "/"]) + .succeeds() + .no_stderr(); +} From 541eb4c6a66023dd3afb594c8de37c29a4903e02 Mon Sep 17 00:00:00 2001 From: Pablo Garcia Date: Mon, 24 Aug 2026 10:28:10 +0200 Subject: [PATCH 2/2] test(find): gate all-slash separator test on Unix The all_slash_pattern_does_not_warn test uses `///` and `/` as starting points, which are root paths only on Unix. On Windows `///` is not a valid path (os error 161, ERROR_BAD_PATHNAME), so find exits 1 before the -name / -path pattern logic runs and the test's `.succeeds()` fails. Gate the test behind cfg(unix), mirroring the existing find_slashes test it references. The warning-exemption logic itself is platform independent and is still covered on Windows by the name_pattern_with_separator_warns and path_pattern_ending_with_separator_warns tests. --- tests/test_find.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_find.rs b/tests/test_find.rs index 8dc3f6cf..2e645199 100644 --- a/tests/test_find.rs +++ b/tests/test_find.rs @@ -1662,8 +1662,11 @@ fn name_pattern_without_separator_does_not_warn() { // A pattern made up only of '/' (e.g. `-name /` or `-path /`) is a legitimate // way to match the root entry, so it must NOT trigger the separator warning // (GNU itself gets this wrong — bug #62227). Covers the existing `find_slashes` -// behavior. +// behavior. Unix-only: `///` and `/` as starting points are root paths on Unix; +// on Windows `///` is not a valid path (os error 161), so the case is exercised +// only on Unix, matching the `find_slashes` test above. #[test] +#[cfg(unix)] fn all_slash_pattern_does_not_warn() { ucmd() .args(&["///", "-maxdepth", "0", "-name", "/"])