Bourne | Ash |  #!  | find | ARG_MAX | Shells | whatshell | portability | permissions | UUOC | ancient | - | ../Various | HOME
"$@" | echo/printf | set -e | test | tty defs | tty chars | $() vs ) | IFS | using siginfo | nanosleep | line charset | locale


About find(1)

emphasizing on portability and the very details of a few basic issues

2016-11-01 (see recent changes)

Table of content

Basic issues:

Availability and origin of some features: A useful combination of find and shell, and a frequent question about it:

Pointers to several system independent implementations.

(What else can you do with find?)


Limit the search to the current directory portably

An often recommended way to limit find(1) to one level (i.e., not descending into directories) is using the expression -maxdepth 1.
This expression was introduced by GNU findutils. FreeBSD 4.1, NetBSD 2.0, OpenBSD 2.0 and the AST toolchest adopted this.
Mac OS X implements it since 10.2 (Darwin 6), switching from NetBSD find to FreeBSD 4.5 find.

But it's also possible to do this with the traditional find 1.

  [1] You need a find utility that offers the expression "-prune".
And nowadays this is generally available, because it had already been introduced with 4.3BSD-Reno and SVR4 (i.e., about 1989).
An exception to this rule is the busybox multi-call binary: it emphasizes on minimalistic replacements. It implements -prune and -maxdepth since june 2007 as a compile time option.

The portable way to avoid descending is

    $ find .      ! -name . -prune <remaining expressions>
    $ find /etc/. ! -name . -prune <remaining expressions>
The first variant is flawless and the remaining text in this section is about the 2nd variant.

Usually, the second works as well (but I know SVR4.0 v2.1 and Cray Unicos as exception).
(But be very careful with other variants like "find /etc [...]" and "find /etc/ [...]".)

The order of arguments is crucial here, because they are not options but expressions.

The explanation sounds obvious: find never lists the '..' entry. If you also exclude the '.' entry and then apply "-prune" to all the remaining entries, find certainly won't descend anymore.
If you ever need the '.' included (pointed out by Stéphane Chazelas in comp.unix.shell), then you can use "find . \( -name . -o -prune \)".

About the portability:

Why using find at all? It's helpful for


You might be able to avoid xargs(1) (with -exec ... {} + )

Another frequently mentioned feature of GNU findutils 3 is a special combination with xargs, "[...] -print0 | xargs -0".
The purpose is increasing performance by avoiding to fork/exec for each single argument;
and the usage of null-terminated filenames avoids problems with unexpected filenames.

  [3] GNU find introduced it with release 2.0 in nov '90.
NetBSD 1.0, FreeBSD 2.0.5, (and thus) Mac OS X came with it from start, and OpenBSD 2.1, the AST toolchest and HP-UX 11.23 incorporated this feature.

However, various find implementations know about the expression -exec + (instead of -exec \;).
This increases performance in the same way, but obsoletes xargs. It's much simpler then:

    $ find . -name xxx -exec command {} +
This has become a standard: it's specified in the SUSv3, aka IEEE 1003.1-2001/2004.
Actually it originates from SVR4 ('88), where it was not documented yet (this feature implemented by D.Korn, see two messages from the austin-group-list, local copies), More implementations came along then:

Implementations without this feature:

Having -exec ... + in mind, the usefulness and elegance of -print0 is debatable:


"Pattern matching" vs "filename expansion" (find . -name '*')

The argument to -name is a pattern. POSIX requires (plain) pattern matching, not filename expansion (file globbing).
Filename expansion is a more special variant of pattern matching, it requires that a leading dot and the slash as pathname separator must be given explicitly.
The shell knows the more common pattern matching in "case $var in pattern)"

So, according to POSIX, "find . -name '*'" shall match leading dots.

On Version 7, find implements matching with glob(3) and thus handles the dot special.
The same applies to System III and V and to the BSDs until 4.3BSD-Tahoe.
In the BSD line, find on 4.3BSD-Reno switched to fnmatch(3), so the dot was not special anymore.

What about later implementations?

The dot is special on

The dot is not special anymore on:


Where do you need "-print"?

The original find(1) required "-print". However, it was changed to be the default action on 4.3BSD-Reno and in POSIX.
It was still required in vanilla System V until (including) SVR4.2, that is, UnixWare 1. I had a look at some implementations:

It's not required on:

It was required on:

Omitting -print:

Be careful with omitting -print when using logical operators (-a or -o):
An implicit -print binds to the whole expression, while an explicit -print binds as if it was added as -a -print.
Example:

    find .    -name omit-directory -prune -o -type f 
    find . \( -name omit-directory -prune -o -type f \) -print

    find .    -name omit-directory -prune -o -type f  -print
    find . \( -name omit-directory -prune \) -o \( -type f -print \) 

BSD fast-find

Another issue where omitting -print can be confusing: the BSD fast-find feature.
It was first implemented in 4.3 BSD: If given only one single argument,

   find filename 
then a database was searched for pathnames containing this filename as component.

But if -print is the default action, this can be confused with the widely known syntax,
where one single argument is interpreted as a directory to descend into.
Thus the feature was removed with 4.3 BSD-Reno and implemented with another utility, locate.

Some variants which implemented the fast-find feature:


Availability of the expression "-ls"?

Version 7 find didn't know "-ls".
It was introduced with 4.3BSD (early '86).
However, it has never been implemented in the vanilla SysV line (up to SVR4.2).
So it was added individually on non-BSDs.

It is for instance available on

It is not available on

By the way, the resulting output imitates "ls -ldis".
It includes inode number and size in blocks (with system specific blocksize), sometimes it's documented as kilobytes.


Availability of the expression "-path"?

Version 7 find didn't know "-path".
It was introduced with 4.4BSD-alpha (mid '91).
It has never been implemented in the SysV line.
Meanwhile it was picked up by SUSv4 (aka POSIX).

It is for example available on

It is not available on


Availability of the expression "-printf"?

Only GNU find seems to implement it as of this writing. The Changelog for GNU find doesn't mention its implementation; so it might have been available from start (02/'87)


Omission of path-list"?

You may omit the path-list (example: "find -print") with But most implementations will require it, that is, at least


Following symbolic links, "-H/-L"

"-H" was introduced with 4.4BSD-alpha; "-L" with 4.4BSD-Lite.
Meanwhile these flags have been specified with SUSv3.

Some implementations that support the options -H/-L:

Not implemented in


Embedded ..{}..

Several modern implementations substitute {} even if it is embedded in a string like this

    find . -exec echo xx{}xx \;

However, the traditional find requires {} to be a separate argument.
The new behaviour was introduced around 386BSD, according to the FreeBSD manpage archive.
POSIX/SUS only specifies {} standing alone, but allows them to be embedded as implementation defined behaviour.

These variants for example accept an embedded {}:

but these don't accept an embedded {}:


What does the x in "find ... -exec sh -c '...' x {}" mean?

This is not concerning find, but differences in shell implementations:
When executing "$SHELL -c 'cmd' x y z ...", how do shells convert the arguments to $0, $1, etc.?
Neverthelesse, it's explained here, because the above combination of find and sh is very versatile, like a swiss army knife.

[4] Shell implementations with the old behaviour about arg0:

Older variants than ksh88f for example exist on
· HP-UX 8-11 ("ksh" but not "sh"): ksh88c
· AIX 3.2: ksh88d
· Ultrix 4.5: ksh88
· Unicos 9: ksh88e
· SVR4.0 v2.1: ksh88d

Older variants of ash exist on
· all traditional BSDs that come with an ash (4.3BSD-Net/2 ... 4.4BSD-Lite2)
· FreeBSD before 2.1.0 (10/95)
· NetBSD before 1.2 (10/96)
· Minix before 3.1.3 (5/06)

[5] GNU mv knows an option to prepend the target and allows to
find [...] -exec mv --target-directory=dir {} +

[sco6] SCO OpenServer 6.0.0 provides three find variants (Thanks to Rodolfo Martín for access):
- /usr/bin/find
- /usr/bin/posix/find
- /u95/bin/find - interestingly more posix compliant than the posix variant, for example concerning pattern matching


System independent find implementations

GNU findutils.

Gunnar Ritter's Heirloom Toolchest implements several traditional variants and POSIX/SUS.

The AT&T AST toolchest is a POSIX/SUS implementation with numerous extensions.

The busybox toolchest is aiming at tiny implementations.

Jörg Schilling's sfind is a POSIX/SUS implementation.


See a link (cat-v.org) to an interesting answer from Dennis M. Ritchie about the history of the syntax of find.


What else can you do with find? Find prime numbers!


<http://www.in-ulm.de/~mascheck/various/find/>
Comments please to mascheck@in-ulm.de, I'd like to hear from you.