Notes on the Linux find command.
Index
- Find multiple file names
- Find ignoring a file
- Find ignoring a file and a directory
- Find files with particular permissions
- Find files not owned by a particular user or group
- Change extension of multiple files
- Dealing with spaces when using find with exec
- Find files excluding a directory
- Find with nested commands
Find multiple file names
find ~ -type f -name "foo" -o -name "bar"
-o
acts as an OR condition.
Find ignoring a file
In this example excluding todo.txt prevents an infinate loop that would otherwise result in todo.txt growing to fill all space on the hard disk.
find . -type f \( -name "*.txt" ! -name "todo.txt" \) -exec grep "TODO" -iHn {} \; > todo.txt
This will search for ‘TODO’ or ‘todo’ in all .txt files except todo.txt and write the output to todo.txt (hence why it’s ignored).
Beware of spaces. There must be spaces between the escaped brackets and the arguments. E.g:
me@pc ~ $ find . -type f \(-name "*.txt" ! -name "todo.txt" \) -exec grep "TODO" -iHn {} \; > todo.txt
find: paths must precede expression: (-name
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
Find ignoring a file and a directory
find . -path "./tmp/*" -prune -o \( -name "*.txt" ! -name "todo.txt" \) -print > todo.txt
This is similar to the earlier examples except also ignores files in the tmp directory.
-prune is carried out if the path is matched, removing them from the results. -o is an OR condition. Everything after this is carried out when the path isn’t matched.
So we’re exluding the directory, then excluding the todo.txt file (as per the earlier example).
Find files with particular permissions
Find all files in /home/steph that are readable for other users:
find /home/steph -perm -o=r
Find all files in /home/steph that are readable or writeable (or both) for the group or others (or both):
find /home/steph -perm +go=rw
Reference: CLI Magic: Searching with find
Find files not owned by a particular user or group
To find files below the current directory not owned by particular user, use the following command (in this example the user is called steph):
find . \! -user steph -print
You can do the same with groups:
find . \! -group steph -print
Reference: Looking for Files NOT owned by someone
Change extension of multiple files
To change the extension of multiple files, e.g. rename
find . -type f -name "*.html.bak" -exec sh -c 'mv {} `basename "{}" .bak`' \;
Dealing with spaces when using find with exec
To prevent problems with spaces in filenames when running the ‘find’ command with -exec, use double quotes:
find . -type f -name "*.html" -exec grep ls "{}" \;
Find files excluding a directory
Find files that contain ‘foo’, excluding those in ‘media’ directory:
find . -type f ! -path "./media/*" -exec grep "foo" -Hn {} \;
Find files that contain ‘foo’, excluding those in ‘media’ and ‘cache’ directories:
find . -type f ! -path "./media/*" ! -path "./cache/*" -exec grep "foo" -Hn {} \;
Prune
Alternatively you can use the -prune
options…
Find files that contain ‘foo’, excluding those in the ‘media’ directory:
find . -path "./media/*" -prune -o -type f -exec grep "foo" -Hn {} \;
Find files that contain ‘foo’, excluding those in the ‘media’ and ‘cache’ directories:
find . -type d \( -path "./media/*" -o -path "./cache/*" \) -prune -o -type f -exec grep "foo" -Hn {} \;
References
Find with nested commands
If you want to run nested commands on the results of a ‘find’, then you’ll have to stop bash expansion running before the find command.
E.g. This won’t work because the shell expansion will happen before the find has set the {} to the filename:
find . -type f -name "*.html.bak6" -exec mv {} `basename "{}" .bak6` \;
This will work because the shell expansion will happen after {} is populated:
find . -type f -name "*.html.bak6" -exec sh -c 'mv {} `basename "{}" .bak6`' \;