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 subdiretories 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.

See find(1) and IEEE Std 1003.1-2017-find for further details and examples.