find
— finding files
The find
utility can be used to search for and perform commands upon
groups of files matching a given set of criteria. The basic usage is
find path rules
.
For portability purposes, always specify a path. Do not rely
upon find
defaulting to the current working directory if no
path is provided.
Useful rules include:
Rule | Effect | POSIX? |
---|---|---|
-name "blah" |
Only find files named blah . The * and ?
wildcards may be used but should be quoted as in -name 'blah*' .
|
yes |
\! -name "blah" |
Only find files not named blah .
|
yes |
-type f |
Find only regular files, not directories. | yes |
-type d |
Find only directories. | yes |
-type l |
Find only symbolic links. | yes |
-user foo |
Find only files belonging to user foo . It is best to use
named users rather than numeric UIDs. In particular, root
may not be UID 0 on some systems.
|
yes |
-group foo |
Find only files belonging to group foo . It is best to use
named groups rather than numeric GIDs.
|
yes |
-maxdepth 3 |
Only descend 3 levels into subdirectories.
-maxdepth 1 will ignore all subdirectories of the specified path.
|
no, GNU and BSD |
-mindepth 2 |
Ignore the first 2 directory levels before a match occurs.
-mindepth 1 will process all files except the command line arguments.
|
no, GNU and BSD |
-exec foo '{}' |
Execute a command. {} is replaced by the name of the
current matched file. See examples below.
|
yes |
-execdir foo '{}' |
Same as -exec but runs the command from within the basedir
of the match.
|
no, GNU and BSD |
-delete |
Delete the match. | no, GNU and FBSD |
-print0 |
Separate file names by a NUL character instead of a newline in the output. This is useful for guarding against files which may include a newline in their names. | no, GNU and FBSD |
By default, find
will echo a list of matching files to the
standard output separated by newlines. It can be combined with a
for
loop for a small number of files with no weird characters
and spaces in their names:
for f in $(find "${S}" -type f) ; do
einfo "Doing unholy things to ${f}"
done
The above loop may cause issues with files containing special
characters in their names. A better way is to run find
with the
-print0
option in a while
loop (note the options passed
to while
and read
for changing the delimiter):
while IFS="" read -d $'\0' -r f ; do
einfo "Calling down holy vengance upon ${f}"
done < <(find "${S}" -type f -print0)
As an alternative you can use the -exec
argument. Be careful
with escaping to ensure that bash
doesn't gobble up the special
characters:
find "${S}" -name '*.data' -exec mv '{}' "${S}/data/" \;
When -exec
is terminated by a ;
character
(needs escaping or quoting) then the command line is built separately
for every match.
If it is terminated by a +
character then the command line is
built by appending each selected file name at the end.
The syntax below is useful if the command you want to run accepts multiple
arguments such as doins
and is more efficient in that case:
find "${S}" -name '*.so*' -exec doexe '{}' +
Find also supports negative matches:
find "${S}/bundled-libs" \! -name 'libbass.so' -delete
This will delete all files in the "bundled-libs" folder except "libbass.so".
Make sure you always escape the !
character, so it's not
interpreted by the shell.
-exec
argument has security issues, because of
race conditions
(this is also true for find . -print0 | xargs ...
constructs).
This should not be a problem in ebuild context, because directories
typically aren't writeable by random users. However you should
consider -execdir
as an alternative approach which runs the
command from inside the basedir of the match (note that -execdir
is not POSIX, see the table).
See find(1)
and
IEEE Std 1003.1-2017-find for further details and examples.