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?

Martin Vegter
  • 358
  • 75
  • 236
  • 411
  • that comes from find, libc has nothing to with that. – Artem S. Tashkinov Aug 09 '22 at 08: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.) – Stephen Kitt Aug 09 '22 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 – Brian Leishman Aug 09 '22 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. – Kamil Maciorowski Aug 09 '22 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 '22 at 01:04
  • find . .. -prune prints . and .., find . .. -maxdepth 1 prints . ./file1... .. ../file2... Would you like it to special-case . and remove the ./ prefixes, for cosmetic purposes at the expense of safety and portability? – Stéphane Chazelas Aug 12 '22 at 08:08
  • @BrianLeishman that should be an answer – phuclv Aug 12 '22 at 08:40

3 Answers3

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.

Stephen Kitt
  • 434,908
  • 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 09 '22 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) – G-Man Says 'Reinstate Monica' Aug 11 '22 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. – G-Man Says 'Reinstate Monica' Aug 11 '22 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. – J. A. Streich Aug 11 '22 at 19:40
  • and, if one really despises the leading ./ then there's always find * ... – keithpjolley Aug 11 '22 at 22:41
  • 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) – Stéphane Chazelas Aug 12 '22 at 08:03
  • @StéphaneChazelas -- agree it's suboptimal. i wouldn't use it. but it's not necessarily wrong depending on use case. – keithpjolley Aug 12 '22 at 20:14
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
Romeo Ninov
  • 17,484
  • i do not have . in my PATH (i prefer to know what i am executing) but i know many people who do. why is it very unwise in a nutshell? – Trevor Boyd Smith Aug 09 '22 at 18:36
  • 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) – Romeo Ninov Aug 09 '22 at 18:48
  • 2
    @TrevorBoydSmith, and this is one of the security controls in hardening lists of recommendations in some huge companies. – Romeo Ninov Aug 09 '22 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. – Trevor Boyd Smith Aug 10 '22 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 '22 at 18:46
  • @ikegami, for this there is --. And how many (normal) people will make files, with name starting with dash? – Romeo Ninov Aug 10 '22 at 18:48
  • 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 '22 at 19:00
  • @ikegami, Check here, its not only GNU: https://unix.stackexchange.com/questions/570729/where-is-the-double-dash-argument-documented – Romeo Ninov Aug 10 '22 at 19:09
  • It's still up to every program that accepts command-line arguments to recognize -- as a special argument, though. There is no requirement (even in POSIX, which I believe mandates it for its own utilities) that any given program support that convention. – chepner Aug 12 '22 at 13:30
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 "-"?

phuclv
  • 2,086
  • 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. – rexkogitans Aug 10 '22 at 05: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 '22 at 07: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'. – G-Man Says 'Reinstate Monica' Aug 14 '22 at 03:39