6

man find:

-f path: Add path to the list of paths that will be recursed into. This is useful when path begins with a character that would otherwise be interpreted as an expression, namely “!” , “(” and “-”.

But why

find ./test-folder -type f -name '*.txt'

works, whereas

find -f ./test-folder -type f -name '*.txt'

doesn't work?


What is the error then?

./test-folder: illegal option -- t
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
       find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
7
  • 7
    (1) "doesn't work" – What is the error then? (2) Are you sure you're using the implementation of find the quoted manual describes? It seems you want FreeBSD find while maybe you're using GNU find for some reason. If it's GNU then find --version will tell you this; what is the output? Commented May 8 at 11:36
  • 1
    In addition to find --version, also run type -a find to see if you have other finds lying around
    – muru
    Commented May 8 at 11:53
  • FWIW, the GNU find (4.9 or newer) equivalent of FreeBSD's find -f "$file1" -f "$file2" ... is find -files0-from <(printf '%s\0' "$file1" "$file2") ... Commented May 8 at 12:40
  • 1
    @KamilMaciorowski (1) I just added it to the question itself. (2) This is just the standard find included with macOS. 14.4.1.
    – jsx97
    Commented May 8 at 15:11
  • @muru type -a find returns find is /usr/bin/find. find --version returns error: find: illegal option -- -
    – jsx97
    Commented May 8 at 15:11

2 Answers 2

7

Let's change the command a bit and see what happens:

% find -f x -type f
x: illegal option -- t
% find -f x -t
x: illegal option -- t
% find -f x -H
[recurses into x] 

I'm not at all sure why it would print the given path in the error message, but apart from that, it's clear that it's the following -t that produces the error. That's because the -type argument is taken as the shorthand for options -t, -y, -p, and -e, the same way something like ls -l -aR has the three options -l, -a, and -R, and not just the option -l and filename -aR. Now, find doesn't have a -t option, so it complains. -H works, as above, and of course find -f . -depth would complain about the option letter e.

You need to end the list of options with -- to get the subsequent args taken as non-options. This is the same with find as with other tools e.g.:

% find -f ./test-folder -- -type f -name '*.txt'

or if you want to list a file called -aR:

% ls -l -- -aR

Of course, with other tools, the issue is mostly with things like ls *.txt where a filename starting with - could be an issue. One seldom uses globs with find, but the idea is the same.

Same as with other tools, one doesn't usually need -- with find, as long as the path given as the first non-option is "nice", e.g. .. An argument not starting with a dash implicitly ends option processing. But you could use --:

% find -- . -type f

Of course you could also use a glob with find, e.g. something like find -- *.d -name "*.conf". Not that the -- helps, since a name starting with a dash (-foo.d) might now be taken as an invalid predicate.

And of course if the expression starts with something that doesn't look like an option (( or !), that also stops option processing, like in the command mentioned in the comments:

find -f ./test-folder \( -type f -name '*.txt' \)

That also doesn't help if a filename starts with a dash, so that's why they have the -f option. Or you could always just prefix the filename with ./, affecting the output similarly. As an added bonus, this would also work with a glob, e.g. ./*.d.


Note that GNU find is different here, it tries to interpret unknown options as predicates:

$ find -H -t
find: unknown predicate `-t'

but of course that's because it doesn't require explicitly giving a path to search, but instead defaults to . if none is given. E.g. these are the same:

$ find -H -type f
...
$ find -H . -type f
...

It also doesn't deal with stacked options, though that's likely fine since one often doesn't use more than one with find (the -H, -L and -P options all control the treatment of symlinks, and override each other, while -O and -D are for optimization and debugging):

$ find -HO1 -type d
find: unknown predicate `-HO1'

It doesn't seem possible to give GNU find a something like -type as a filename on the command line. You'll have to use ./-type instead, or pass the filenames through -files0-from.

8
  • 1
    See also the Prefix parameter expansion flag of zsh in case you wanted to pass a glob to FreeBSD find's -f. Like find *-<1-1000>.d(FP[-f]) -- ... (though you likely don't need find if you're using zsh) Commented May 9 at 8:35
  • 1
    With GNU find (4.9+), you can do find -files0-from <(printf '-type\0') to have -type as the filename. See also my comment to the question. Commented May 9 at 8:36
  • @StéphaneChazelas "Though you likely don't need find if you're using Zsh" - I use Zsh. Which Zsh alternatives to find would you suggest? After reading your comment, I discovered locate, but maybe Zsh provide something even better?
    – jsx97
    Commented May 9 at 9:13
  • @jsx97, zsh globs can do most of what find can do through its recursive globbing, extended globs and glob qualifiers. For example, your find ./test-folder -type f -name '*.txt' is just ./test-folder/**/*.txt(D.) (D to include hidden files to match find's default behaviour. You may not need it, . for -type f) Commented May 9 at 10:57
  • @StéphaneChazelas Thanks, Stéphane. Out of curiosity, do you prefer it over locate?
    – jsx97
    Commented May 9 at 12:10
4

Use -- to end the list of search paths, before specifying any find expressions:

find -f ./test-folder -- -type f -name '*.txt'

The -- usually signals the end of options, and macOS find uses this in conjunction with the -f option to signal the end of search paths. This is alluded to in the "BUGS" section of the find(1) manual on macOS (my emphasis):

As there is no delimiter separating options and file names or file names and the expression, it is difficult to specify files named -xdev or !. These problems are handled by the -f option and the getopt(3) -- construct.

3
  • Thanks a lot. If you don' mind, could you explain why using a trailing slash at the end of "where" folder, that is, -f ./test-folder/ instead of -f ./test-folder produces a double slash in search results, that is, ./test-folder//file.txt? As far as I know, using a slash at the end of a folder is commonly considered a good practice, but find is trying to break this consistency. And does GNU find has the same issue?
    – jsx97
    Commented May 8 at 16:24
  • 1
    @jsx97 When find outputs a found pathname, it uses the given search path as-is, with the path to the found name appended to the end of it. If give the search path with a slash at the end (which you may want to do to either resolve a symbolic link (but you could also use find -H for this) or to ensure that the path is that of a directory), you get output that contains doubled-up slashes. There is no issue with this as a double (or triple, or whatever) slash inside a pathname is the same as a single slash.
    – Kusalananda
    Commented May 8 at 17:16
  • @jsx97 With regards to double slashes in pathnames, see also unix.stackexchange.com/questions/1910/…
    – Kusalananda
    Commented May 8 at 17:19

You must log in to answer this question.

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