14

Why is it that find prints out a leading ./ to results if no paths are given?

$ find
./file1
./file2
./file3

What is the reason for not printing out this?

$ find
file1
file2
file3

4 Answers 4

17

The reason why you see this is because the developer of GNU find chose to provide a "reasonable" behavior for find when no path is given. In contrast, POSIX doesn't state that the parameter is optional:

The find utility shall recursively descend the directory hierarchy from each file specified by path, evaluating a Boolean expression composed of the primaries described in the OPERANDS section for each file encountered. Each path operand shall be evaluated unaltered as it was provided, including all trailing <slash> characters; all pathnames for other files encountered in the hierarchy shall consist of the concatenation of the current path operand, a <slash> if the current path operand did not end in one, and the filename relative to the path operand. The relative portion shall contain no dot or dot-dot components, no trailing characters, and only single <slash> characters between pathname components.

You can see the difference in the synopsis for each. GNU has (as is the convention) optional items in square brackets:

find [-H] [-L] [-P] [-D debugopts] [-Olevel] [starting-point...]
       [expression]

while POSIX doesn't indicate that it can be optional:

find [-H|-L] path... [operand_expression...]

In the GNU program, that's done in ftsfind.c:

  if (empty)
    {
      /*
       * We use a temporary variable here because some actions modify
       * the path temporarily.  Hence if we use a string constant,
       * we get a coredump.  The best example of this is if we say
       * "find -printf %H" (note, not "find . -printf %H").
       */
      char defaultpath[2] = ".";
      return find (defaultpath);
    }

and a literal "." is used for simplicity. So you'll see the same result with

find

and

find .

because (and POSIX agrees) the given path will be used to prefix the results (see above for concatenation).

With a little work, one could determine when the feature was first added; it was present in the initial creation of "findutils" in 1996 (see find.c):

+  /* If no paths are given, default to ".".  */
+  for (i = 1; i < argc && strchr ("-!(),", argv[i][0]) == NULL; i++)
+    process_top_path (argv[i]);
+  if (i == 1)
+    process_top_path (".");
+
+  exit (exit_status);
+}

From the changelog for find 3.8, this was apparently

Sat Dec 15 19:01:12 1990  David J. MacKenzie  (djm at egypt)

        * find.c (main), util.c (usage): Make directory args optional,
        defaulting to "."
0
11

Usually, one does post-processing of the files and, in that case, there can be a huge advantage to starting the filename with ./. In particular, if a file name starts with -, a subsequent command could interpret that filename an option. ./ avoids that.

As an example, consider a directory with these files:

$ ls
--link  --no-clobber

Now, imagine how this command would work if the file names were provided without the ./ in front:

$ find -type f -exec cp -t ../ {} +

We can illustrate the problem with find itself. Let's run it in the same directory as above. The following works:

$ find ./*
./--link
./--no-clobber

The following fails:

$ find *
find: unknown predicate `--link'
Try 'find --help' for more information.
10
  • 1
    It makes sense. But then there's the question why it doesn't prepend a '.' when you run find *.
    – n.r.
    Aug 5, 2016 at 17:10
  • @n.r. Good point. I expect that it behaves that way for some kind of historical compatibility. I added to the answer an example of why this is undesirable behavior.
    – John1024
    Aug 5, 2016 at 17:13
  • 3
    Some versions of file demand the user to give a path (like the BSD find on OS X). So you usually need to explicitly say something like find . -type f .... From there, it's not a big step for some versions of find (like GNU find) to just default to . and leave everything else as is.
    – ilkkachu
    Aug 5, 2016 at 17:23
  • 1
    The reason for find * not showing the . is because * lists all files and folders, but excludes .. Do echo * in a directory that contains only one or two files, and you will see that . is not listed. Thus, find * operates on each file expanded. It's the same as if you said find Desktop/ from home directory. You will see output as Desktop/foo_bar.txt Aug 6, 2016 at 5:28
  • 1
    @John1024:  I believe that Mrigesh and Thomas Dickey have correctly answered the question.  This answer states why it’s convenient that find behaves the way it does.  Do you have any authoritative reference information to support the implied claim that find was designed to behave in this way for this reason? Aug 6, 2016 at 21:19
4

The find command needs path(s) to search.  If we don't specify any, it uses the current directory (.) as its starting point.  Similarly, if you pass the path, e.g., /tmp, it considers that as its starting point.  And therefore the results.

If current directory:

        $ find
or
        $ find .

output:
        ./file1
        ./file2
        ./file3

If /tmp directory:

        $ find /tmp

output:
        /tmp/file4
        /tmp/file5

If abc directory under the current directory:

        $ find abc

output:
        abc/file6
        abc/file7

If multiple directories under the current directory:

        $ find fu bar

output:
        fu/file10
        fu/file11
        bar/file8
        bar/file9
2
  • Yes, I agree find needs a path to search anything and that it defaults to the current directory. The question is why it prints out the leading ./ when file.txt is just the same as ./file.txt.
    – n.r.
    Aug 5, 2016 at 17:32
  • 1
    its not that find adds "." at start actually it adds whatever you give it as path, whether, it is "/tmp" "abc" or ".". It will return all values respectively. Aug 5, 2016 at 17:48
-2

If you do not specify a path, find command assumes the ${PWD} as the path and prints it out on its output. User not specifying the path doesn't change the way find works. And find always works with paths by default.

5
  • 1
    I see. But if you execute it under /tmp, then $PWD is /tmp not ./.
    – n.r.
    Aug 5, 2016 at 17:04
  • if you want to see preceeding /tmp, run the command find /tmp If you do not specify a path it will always be current directory, which is ./
    – MelBurslan
    Aug 5, 2016 at 17:06
  • 1
    It's not that I want to see preceeding /tmp. It's that it can't be $PWD.
    – n.r.
    Aug 5, 2016 at 17:08
  • My apologies ${PWD} was the incorrect verbiage
    – MelBurslan
    Aug 5, 2016 at 17:09
  • 2
    No, it doesn't assume $PWD. Compare the output of find ., find $PWD and find (without a path, if your find supports it).
    – ilkkachu
    Aug 5, 2016 at 17:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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