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
n.r.
  • 2,243

4 Answers4

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 "."
Thomas Dickey
  • 76,765
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.
John1024
  • 74,655
  • 1
    It makes sense. But then there's the question why it doesn't prepend a '.' when you run find *. – n.r. Aug 05 '16 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 05 '16 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 05 '16 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 – Sergiy Kolodyazhnyy Aug 06 '16 at 05:28
  • @n.r. Same reason that find foo doesn't prepend ./ - it prepends foo/ – user253751 Aug 06 '16 at 08:52
  • Fine. But then this doesn't prevent post-processing of -files from getting interpreted as flags to find itself. – n.r. Aug 06 '16 at 13:32
  • @Serg: I believe that you’re misinterpreting the OP’s follow-up question. It’s not “Why doesn’t find * print . .. file1 file2 file3?”; it’s “Why doesn’t find * print ./file1 ./file2 ./file3?”. immibis (and Mrigesh and Thomas Dickey) have answered that question. – G-Man Says 'Reinstate Monica' Aug 06 '16 at 21:16
  • 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? – G-Man Says 'Reinstate Monica' Aug 06 '16 at 21:19
  • I understand his follow-up question correctly. And like i said, it has nothing to do with find but with the*. Do the thing withecho * and you'll see – Sergiy Kolodyazhnyy Aug 06 '16 at 21:20
  • Also @G-Man note that find * will print only file1 file2 file3 . It will not print . .. file1 file2 file3 – Sergiy Kolodyazhnyy Aug 06 '16 at 21:30
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
  • 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 05 '16 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. – Mrigesh Priyadarshi Aug 05 '16 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.

MelBurslan
  • 6,966