10

Why are some relative file paths displayed in the form of ./file, instead of just file? For example, when I do:

find .

I get this output:

./file1
./file2
./file3

What is the practical purpose, other than making the path more confusing? It's not like it is preventing me from some accident. Both are relative paths, and cat ./file1 works same as cat file1.

Is this behavior coming from find command, or is it some system-wide c library?

OK, I understand why using ./file for -exec construct is necessary (to make sure I have ... | xargs rm ./-i, and not ... | xargs rm -i).

But in what situation would missing ./ break anything when using -print statement?

I am trying to construct any statement that breaks something:

touch -- -b -d -f -i
find -printf '%P\n' | sort

-b
-d
-f
-i

Everything works fine.

Just out of curiosity, how could I construct a -print statement that would demonstrate this issue?

7
  • that comes from find, libc has nothing to with that. Aug 9, 2022 at 8:15
  • 4
    Your update mixes -exec and xargs. The latter shows the problem with -print. If you use -exec, you don’t need xargs; if you’re using xargs, you’d end up with find -printf '%P\0' | xargs -0 rm which would break. (It’s trivially fixable with rm -- on systems where rm recognises --, but hopefully it still demonstrates the issue.) Aug 9, 2022 at 11:41
  • 2
    you can install .deb with apt. Example apt install discord installs from sources, and apt install ./discord installs local file called "discord" in the current dir Aug 9, 2022 at 19:55
  • 7
    "What is the practical purpose, other than making the path more confusing?" – Making the path less confusing. I mean for me ./something is less confusing than something. The latter does not have to be a path, but the former almost certainly is. Aug 9, 2022 at 21:09
  • 4
    This is Unix. The output of -print could be the input to anything. find doesn't make assumptions.
    – hobbs
    Aug 10, 2022 at 1:04

3 Answers 3

37

This behaviour comes from find, and is specified by POSIX:

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 default action, -print, outputs the full pathname to standard out.

find outputs the paths of files it finds starting from the path(s) given on its command line.

find .

asks find to look for files under . and its subdirectories, and it presents the results starting with ./;

find foo

would do the same but starting with foo, and it would produce results starting with foo/.

I don’t think find does this specifically to prevent problems with un-prefixed file names; rather, it does this for consistency — regardless of the path provided as argument, the output of -print always starts with that path.

With the GNU implementation of find, you can strip the initial path off the start of the printed file by using -printf '%P\n' in place of -print. For instance with find foo/bar -name file -printf '%P\n' or find . -name file -printf '%P\n', you'd get dir/file instead of foo/bar/dir/file or ./dir/file for those files.

More generally, having ./ as a prefix can help prevent errors, e.g. if you have files with names starting with dashes; for example if you have a file named -f, rm -f won’t delete it, but rm ./-f will.

When running commands with a shell or with exec*p() standard C functions (and their equivalent in other languages), when the command name doesn't contain a /, the path of command is looked in $PATH instead of being interpreted as a relative path (the file in the current working directory). Same applies for the argument to the . / source special builtins of several shells (including POSIX compliant sh implementations). Using ./cmd in that case instead of cmd, which is another way to specify the same relative path, but with a / in it is how you typically invoke a command stored in the current working directory.

7
  • 14
    "rather, it does this for consistency" -- One could argue that printing foo/ is not necessary either, but it is because find can accept multiple starting points, which I think is the key reason. For example, the need to include the prefix is more apparent with find . foo bar.
    – JoL
    Aug 9, 2022 at 17:29
  • 1
    @JoL: I strongly dispute your hypothetical argument.  Every output from find … -print can, in principle, be used as an argument to another command (such as cat, grep, ls or stat) via a mechanism such as copy & paste or xargs.  (Let’s, for the moment, set aside the problems with names that begin with -, contain newline(s), and other oddities.)  … (Cont’d) Aug 11, 2022 at 18:24
  • 1
    (Cont’d) …  If find finds file1 in ., then it can certainly be argued that saying file1 and saying ./file1 are equally useful.  But, if find foo/bar finds file42 in subdirectory baz, then outputting foo/bar/baz/file42 is much more useful than outputting just file42 or baz/file42. Aug 11, 2022 at 18:24
  • @G-ManSays'ReinstateMonica' I agree, and is more useful for piping in scripts to avoid prepending to the output of the print to some other command. If I want to do something with all found files, I don't need to prepend the path from the working directory onto it. Aug 11, 2022 at 19:40
  • 1
    @keithpjolley, find * is wrong / suboptimal for several reasons: it skips hidden files (in the current directory only), it behaves badly if there's no non-hidden file in the current directory. It chokes on file names that start with - or are find predicates. It may fail if there are too many files in the current directory (note that compared to find ., it also sorts the list of files) Aug 12, 2022 at 8:03
11

There is no practical use when we talk about ordinary files. But when we talk about executable files it make sense. When you execute (and file1 is located in current directory):

file1

shell search for executable file in all the paths in your PATH variable. And will execute this file only if you have in PATH something like: :.: (current directory is in PATH search). Instead of this very unwise setting you can run file1, located in current directory by:

./file1
9
  • 2
    @TrevorBoydSmith, it open a backdoor to execute command located in current directory. This can be used for malicious activities (simplify them). Or can lead to actions by mistake (which actions can be sometime devastated) Aug 9, 2022 at 18:48
  • 2
    @TrevorBoydSmith, and this is one of the security controls in hardening lists of recommendations in some huge companies. Aug 9, 2022 at 18:49
  • 1
    very well said regarding backdoor to execute command located in current directory. that in itself is a huge security issue i think. the secondary reason is the one i talked about lead to actions by mistake. also good to know about best practices recommendation. Aug 10, 2022 at 18:01
  • 1
    Re "There is no practical use when we talk about ordinary files.", It can, actually. Consider a file whose name starts with -.
    – ikegami
    Aug 10, 2022 at 18:46
  • 1
    @Romeo Ninov, Yes, GNU has --. But GNU isn't everything. And while -- can be used, it rarely is. They assume, like you, that they will never encounter a file starting with -. But that overlooks maliciousness and mistakes (such as copying text into a terminal that results in weird file names getting created). Prepending the path nicely avoids all of that.
    – ikegami
    Aug 10, 2022 at 19:00
5

Many commands in Linux receive - as a special file denoting stdin, therefore to indicate a file whose name is really - you must use ./-. See

Besides - is also used for arguments, so if you want to work with file names starting with - like --file you have to use ./--file. That's also why if you want to grep a pattern starting with - you must use grep -e --pattern. See How to "less" a file named "-"?

3
  • Everything but the last point is correct. The reason why you have to use grep --pattern if the pattern begins with a dash is because otherwise the pattern will be confused with an argument, it is not a filename which will be confused with anything. Aug 10, 2022 at 5:37
  • a pattern beginning with - will be confused with an argument, exactly the same as a file name beginning with -. That's why some method to differentiate them with a normal argument. What's wrong with that?
    – phuclv
    Aug 10, 2022 at 7:17
  • (1) Talking about other potential arguments that begin with - is wandering off-topic, since you’re protecting them with -e and not ./. (2) It’s confusing when you say you’re talking about things “starting with -” but then show only examples starting with -- — especially when you say --pattern rather than --pattern or something like '-foo.*bar'. Aug 14, 2022 at 3:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .