Why does find print a leading './' if no paths are given?

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 "."

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.

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

Tags:

Find