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