Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

On Linux,

cd /tmp
mkdir foo; cd foo

Now, running

find . -name 'foo'

gives no output. Whereas running

find /tmp/foo -name 'foo'

Gives the output /tmp/foo which doesn't make sense to me. Can somebody explain why?

Thanks in advance!

share|improve this question
2  
find seaches inside the given path included as it is stated. So if you input ./ it didn't match foo – Costas 11 hours ago
    
@Costas: I understand that. What I don't understand is why it has made a difference if I give the absolute path to find. – York 11 hours ago
    
@York In certain situations there is no behaviour that is always right. Imagine a symlink with the name bar that points to a file foo which is outside the search path. Shall that match or not? – Hauke Laging 10 hours ago
    
The . and /tmp/foo are not the same - they are two different hard links to the same directory; find /tmp/foo/. -name 'foo' doesn't find anything either. – jimmij 10 hours ago
    
@jimmij: when I run find /tmp/foo -name 'foo', I was asking bash to find in the directory /tmp/foo, a file whose name is "foo". Because the directory /tmp/foo is empty, it should have returned nothing. I don't understand why it returns /tmp/foo. On the other hand, when I run find . -name 'foo', I was asking bash the same thing, i.e., finding a file in the current directory (which happened to be /tmp/foo), whose name is 'foo', and it returns nothing which makes sens. – York 10 hours ago

There is no normalization of the command line arguments before the tests are applied. Thus the results differ depending on the used path (if symlinks are involved):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

In "your case" both calls would give the same result which might be (more) confusing. You can use -mindepth 1 if you want the starting points ignored (may be non-POSIX).

share|improve this answer
    
-mindepth 1 works. However, I still don't understand why find . -name 'foo' and find /tmp/foo -name 'foo' behave differently. – York 10 hours ago

There is no object named foo in the directory where you started the relative search.

You are correct with assuming that gfind is buggy when it reports /tmp/foo when using the absolute name as a start directory.

Gfind has various deviations from the standard, it seems you found another one. When you like a more standard compliant solution, I recommend sfind that is part of the schilytools.

-name applies to directory search results. None of the two cases you mention will return a directory entry foo from a readdir() operation.

share|improve this answer
    
I suspect this is a bug too. Here's the output from find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <gnu.org/licenses/gpl.html>; – York 10 hours ago
    
Well, I checked your example commands with find (POSIX certified), gfind and sfind; the problem only exists when you use gfind. On Linux, gfind is not installed under it's native name but under the name find. – schily 10 hours ago
    
Thanks for confirming – York 9 hours ago
1  
It is correct for find /tmp/foo -name foo to output /tmp/foo. (Cc @York) This is the behavior of OpenBSD find, GNU find, BusyBox find, Solaris find, and any other POSIX-compliant find (“The primary shall evaluate as true if the basename of the filename being examined matches pattern (…)” — when the filename is one that's passed as a path operand, no readdir call is involved). – Gilles 4 hours ago

find traverses the specified directory tree(s), and evaluates the given expression for each file that it finds. The traversal starts at the given path. Here's a summary of how find . -name foo operates:

  • First path on the command line: .
    • Does the base name (.) match the pattern foo? No, so do nothing.
      It so happens that /tmp/foo is another name for the same directory. But find doesn't know that (and it isn't supposed to try to find out).
    • Is the path a directory? Yes, so traverse it. Enumerate the entries in ., and for each entry, perform the traversal process.
      • The directory is empty: it contains no entry other than . and .., which find does not traverse recursively. So the job is finished.

And find /tmp/foo:

  • First path on the command line: /tmp/foo
    • Does the base name (foo) match the pattern foo? Yes, so the condition matches.
      • There is no action associated with this condition, so perform the default action, which is to print the path.
    • Is the path a directory? Yes, so traverse it. Enumerate the entries in /tmp/foo, and for each entry, perform the traversal process.
      • The directory is empty: it contains no entry other than . and .., which find does not traverse recursively. So the job is finished.

It so happens that . and /tmp/foo are the same directory, but that's not enough to guarantee that find has the same behavior on both. The find command has ways to distinguish between paths to the same file; the -name predicate is one of them. find /tmp/foo -name foo matches the starting directory as well as any file underneath it that's called foo. find . -name . matches the starting directory only (. can never be found during a recursive traversal).

share|improve this answer

(gnu) find shows any matches found within the path provided to the command because it starts its comparison with the commandline arguments, descending deeper into the directory structure from there (thus, -maxdepth 0 confines the tests to the base level or the commandline arguments only, whereas -mindepth 1 skips the commandline arguments as man find explains). This is the reason why find /tmp/foo -name 'foo' will yield one match even if the directory itself is empty.

find . -name 'foo' on the other hand will not yield any result because . (dot) is a special file that acts like a hardlink to the same inode as /tmp/foo - it is like a seperate (though special) filename and not a symbolic link or an expression that is subjected to pathname expansion by the shell. Therefore, the first test applied by find to the commandline arguments in the given example will not show any matches, since . indeed does not match the name pattern defined in -name 'foo'. Neither does /tmp/foo/. since a test for a -name pattern is performed on the basename of the path only (see man find), which here again is ..

While this behavior may not be expected or appear intuitive from a user perspective (and yes, it had me confused at first as well), it does not constitute a bug but corresponds with the logic and functionality described in the man and info pages for (gnu) find.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.