Find command
The easiest one is probably to use the locate command. This command uses a cache of known files. In order to use it, you need to make sure the cache is up-to-date, using updatedb. In your case, you will have to trust that it is up-to-date since you are not root and cannot update the db. To use locate, type:
$ locate MyMissingFile
If you want only files called exactly "MyMissingFile", use:
$ locate MyMissingFile | grep "/MyMissingFile$"
$ locate MyMissingFile
If you want only files called exactly "MyMissingFile", use:
$ locate MyMissingFile | grep "/MyMissingFile$"
locate project
Lists all files that contain the string "project". If that command does not work you will need to run the command: slocate -u
This command builds the slocate database which will allow you to use the locate (slocate) command. It may take a few minutes to run.
Now if you know in which directory your file is, you can use find. find does not rely on a cache db, and will scan recursively the directory and print all the files it finds. There are tons of options to filter the found files (see man find to list them), including a -name option to filter on the file name. Use it as such:
$ find $yourdirectory -name MyMissingFile
Find command is used to search dynamically in linux systesm.
The general syntax of find command is as follows.Find command support standard UNIX regex to match or exclude files. You can write complex queries easily with regex.
#find [Directory] [criteria]
Directory is where we want to search
Criteria is what we are giving to search. Eg filename, username, Inode number etc.
The following command will search a file with filename file_name in current working directory.
#find -name file_name
The default nature of the find is case sensitive. if u search for file abc, it will list only abc. Not ABC,Abc, abC.
The default nature of the find is case sensitive. if u search for file abc, it will list only abc. Not ABC,Abc, abC.
To make it case insensitive use "-iname" instead of "-name"
#find -iname file_name
The following command will search a file with filename file_name in /etc and its subdirectories.
#find /etc -name file_name
The following command will search a file with filename file_name in / and its subdirectories, i.e the whole system.
#find / -name file_name
Find all directories
Find Files Based on file-type using option -type:
Find only the socket files.
# find . -type s
Find all directories
# find . -type d
Find only the normal files
# find . -type f
Find all the hidden files
# find . -type f -name ".*"
Find all the hidden directories
# find -type d -name ".*"
# find . -type f
Find all the hidden files
# find . -type f -name ".*"
Find all the hidden directories
# find -type d -name ".*"
The following command will search for all the files & directories whose owner is ashish.
#find / -user ashish
#find / -user ashish
#find ~ashish user ashish [ List files in home directory ]
#find /home -user ashish
The following command will search for all the files & directories whose owner is ashish and group is ashley.
#find / -user ashish -group ashley
#find / -user ashish -group ashley
The following command will search for all the files & directories whose owner is ashish or ash.
#find / -user ashish -o -user ash
#find / -user ashish -o -user ash
The following command will search for all the files & directories whose owner is randeep and group is NOT ashio.
#find / -user ashish -not -group ashio
#find / -user ashish -not -group ashio
Find command can be used for finding files with specific permissions.
The following command will find the files and directories with 755 permissions.
#find / -perm 755
The following command will find the files and directories in which anyone can write.
#find / -perm +2
The following command will find the files and directories in which everyone can write.
#find / -perm -2
The following command will find the files and directories in which others can write.
#find / -perm +o+w
The following command will find the files and directories in which others can not write.
#find / -perm +o-x
Find can be used to find the files with specified size.
The followng command will list the files with size 10M [9.5-10.5]
#find / -size 10M
The followng command will list the files with size less than 10M.
#find / -size -10M
The followng command will list the files with size greater than 10M.
#find / -size +10M
Find a file "foo.bar" that exists somewhere in the filesystem
#find / -name foo.bar -print
If the file is found the path to the file will be printed as output. On most platforms the -print is optional, however, on some Unix systems nothing will be output without it. Without specifications find searches recursively through all directories.
#find / -name foo.bar -print
If the file is found the path to the file will be printed as output. On most platforms the -print is optional, however, on some Unix systems nothing will be output without it. Without specifications find searches recursively through all directories.
Find a file without searching network or mounted filesystems
# find / -name foo.bar -print -xdev
This is useful if you have mounted network drives or filesystems that you do not want searched. This can increase search speed greatly if the mounted filesystem is large or over a slow network. "-mount" does the same thing as "-xdev" for compatibility with other versions of find.
# find / -name foo.bar -print -xdev
This is useful if you have mounted network drives or filesystems that you do not want searched. This can increase search speed greatly if the mounted filesystem is large or over a slow network. "-mount" does the same thing as "-xdev" for compatibility with other versions of find.
Find a file, who's name ends with .bar, within the current directory and only search 2 directories deep
# find . -name *.bar -maxdepth 2 -print
Search directories "./dir1" and "./dir2" for a file "foo.bar"
#find ./dir1 ./dir2 -name foo.bar -print
# find . -name *.bar -maxdepth 2 -print
Search directories "./dir1" and "./dir2" for a file "foo.bar"
#find ./dir1 ./dir2 -name foo.bar -print
Find a file that is a certain type. "-type l" searches for symbolic links
# find /some/directory -type l -print
Several types of files can be searched for:
# find /some/directory -type l -print
Several types of files can be searched for:
b block (buffered) special c character (unbuffered) special d directory p named pipe (FIFO) f regular file l symbolic link s socket D door (Solaris)
Search for directories that contain the phrase "foo" but do not end in ".bar"
#find . -name '*foo*' ! -name '*.bar' -type d -print
#find . -name '*foo*' ! -name '*.bar' -type d -print
Find any file whose name ends with either 'c' or 'asm', enter:
$ find . -type f \( -iname "*.c" -or -iname "*.asm" \)
The parentheses must be escaped with a backslash, "\(" and "\)", to prevent them from being interpreted as special shell characters. The -type f option force to only search files and not directories. The or operator either find .c or .asm file.
$ find . -type f \( -iname "*.c" -or -iname "*.asm" \)
The parentheses must be escaped with a backslash, "\(" and "\)", to prevent them from being interpreted as special shell characters. The -type f option force to only search files and not directories. The or operator either find .c or .asm file.
The "!" allows you to exclude results that contain the phrases following it.
$ find ~/documents -type f -name '*.txt' \ -exec grep -s DOGS {} \; -print
|
This sequence uses find to look in /users/home/directory/documents for a file (-type f) with a name ending in .txt. It sends the files it finds to the grep command via the -exec grep searches the file found for any occurrences of the word "DOG". If the file is found it will be output to the screen and if the word "DOG" is found, within one of the found files, the line that "DOG" occurs in will also be output to the screen.
grep -s : --no-messages suppress error messages
Find and Access times
A file has three kind of times.
atime - access time. When file was last read
mtime - modified time. When file was last modified.
ctime - Change in metadata. When file metadata last changed.
Following command will list the files which are accessed five days ago.
#find / -atime 5
Following command will list the files which are accessed less than five days ago.
#find / -atime -5
Following command will list the files which are accessed more than five days ago.
#find / -atime +5
Following command will list the files which are modified less than 10 days ago
#find / -mtime -10
Find files in your home directory which have been modified in the last twenty-four hours.
#find $HOME -mtime 0
Find files in your home directory which have been modified in the last twenty-four hours.
#find $HOME -mtime 0
This command works this way because the time since each file was last modified is divided by 24 hours and any remainder is discarded. That means that to match -mtime
0, a file will have to have a modification in the past which is less than 24 hours ago.
#find -mtime +1 says to match files modified two or more days ago.
#find -mtime +1 says to match files modified two or more days ago.
#find . -mtime 0 # find files modified between now and 1 day ago (i.e., within the past 24 hours)
#find . -mtime -1 # find files modified less than 1 day ago (i.e., within the past 24 hours, as before)
#find . -mtime 1 # find files modified between 24 and 48 hours ago
#find . -mtime +1 # find files modified more than 48 hours ago
#find . -mmin +5 -mmin -10 # find files modified between 6 and 9 minutes ago
One of my favorite of the find criteria is used to locate files modified less than 10 minutes ago. I use this right after using some system administration tool, to learn which files got changed by that tool:
find / -mmin -10
(This search is also useful when I've downloaded some file but can't locate it, only in that case -cmin may work better. Keep in mind neither of these criteria is standard; -mtime and -ctime are standard, but use days and not minutes.)
Following command will list the files which are changed metadata less than 10days ago. Changing timestamp, permissions etc.
#find / -ctime -10
In the same way, following example finds all the files (under root file system /) that got changed within the last 24 hours (1 day).
Find all files of a given type from current directory on down:
#find ./ -name "*.conf" -print
Find all user files larger than 5Mb:
#find /home -size +5000000c -print
Find all world writable directories:
#find / -perm -0002 -type d -print
Find all world writable files:
#find / -perm -0002 -type f -print
#find / -perm -2 ! -type l -ls
Find files with no user:
#find / -nouser -o -nogroup -print
Find files modified in the last two days:
#find / -mtime 2 -o -ctime 2
Find all files owned by a user (defined by user id number. see /etc/passwd) on the system: (could take a very long time)
#find / -user 501 -print
Find all files created or updated in the last five minutes: (Great for finding effects of make install)
#find / -cmin -5
Find all users in group 20 and change them to group 102: (execute as root)
#find / -group 20 -exec chown :102 {} \;
Following example will find files in the current directory and sub-directories, which changed within last 1 hour (60 minutes)
# find . -cmin -60
In the same way, following example finds all the files (under root file system /) that got changed within the last 24 hours (1 day).
# find / -ctime -1
Find all files of a given type from current directory on down:
#find ./ -name "*.conf" -print
Find all user files larger than 5Mb:
#find /home -size +5000000c -print
Find all world writable directories:
#find / -perm -0002 -type d -print
Find all world writable files:
#find / -perm -0002 -type f -print
#find / -perm -2 ! -type l -ls
Find files with no user:
#find / -nouser -o -nogroup -print
Find files modified in the last two days:
#find / -mtime 2 -o -ctime 2
Find all files owned by a user (defined by user id number. see /etc/passwd) on the system: (could take a very long time)
#find / -user 501 -print
Find all files created or updated in the last five minutes: (Great for finding effects of make install)
#find / -cmin -5
Find all users in group 20 and change them to group 102: (execute as root)
#find / -group 20 -exec chown :102 {} \;
Following command will list the files which are newer than abc.txt
#find -newer abc.txt
Following command will list the files which are older than abc.txt
#find -not -newer abc.txt
Suppose we want to take the backup of all the text files in the system. The following command will search for text files and copy each to the dir /text_files.
#find / -name "*.txt" -exec cp {} /text_files \;
The following command will search for configuration files and copy each to the dir /conf_files
#find / -name "*.conf" -exec cp {} /conf_files \;
The following command will search for the file with inode number 23453 and will delete it.
#find / -inum 23453 -exec rm -rf {}\;
Other options
To search only in the partition where present working directory belongs to.
#find / -xdev -name abc.txt
To search for only the directories of name abc.
#find / -type d -name abc
To search for only the files of name abc.
#find / -type f -name abc
How do I ignore hidden .dot files while searching for files?
Find *.txt file but ignore hidden .txt file such as .vimrc or .data.txt file:#find . -type f \( -iname "*.txt" ! -iname ".*" \)
Find all .dot files but ignore .htaccess file:
# find . -type f \( -iname ".*" ! -iname ".htaccess" \)
which will match files whose names are exactly "MyMissingFile". To match names that contain "MyMissingFile", use:
$ find $yourdirectory -name "*MyMissingFile*"
Search and list all files from current directory and down for the string ABC:
#find ./ -name "*" -exec grep -H ABC {} \;
#find ./ -type f -print | xargs grep -H "ABC" /dev/null [ grep -H: Print file name for each match ]
#egrep -r ABC *
Find all suid and setgid executables:
#find / \( -perm -4000 -o -perm -2000 \) -type f -exec ls -ldb {} \;
#find / -type f -perm +6000 -ls
Search and list all files from current directory and down for the string ABC:
#find ./ -name "*" -exec grep -H ABC {} \;
#find ./ -type f -print | xargs grep -H "ABC" /dev/null [ grep -H: Print file name for each match ]
#egrep -r ABC *
Find all suid and setgid executables:
#find / \( -perm -4000 -o -perm -2000 \) -type f -exec ls -ldb {} \;
#find / -type f -perm +6000 -ls
- Note: suid executable binaries are programs which switch to root privileges to perform their tasks. These are created by applying a "sticky" bit: chmod +s. These programs should be watched as they are often the first point of entry for hackers. Thus it is prudent to run this command and remove the "sticky" bits from executables which either won't be used or are not required by users. chmod -s filename
find . -type f -exec file '{}' \;
Runs `file' on every file in or below the current directory. Notice that the braces are enclosed in single quote marks to protect them from interpretation as shell script punctuation. The semicolon is similarly protected by the use of a backslash, though ';' could have been used in that case also.
find / \( -perm -4000 -fprintf /root/suid.txt '%#m %u %p\n' \) , \ \( -size +100M -fprintf /root/big.txt '%-10s %p\n' \)
Traverse the filesystem just once, listing setuid files and directories into /root/suid.txt and large files into /root/big.txt.
find . -perm 664
Search for files which have read and write permission for their owner, and group, but which other users can read but not write to. Files which meet these criteria but have other permissions bits set (for example if someone can execute the file) will not be matched.
find . -perm -664
Search for files which have read and write permission for their owner and group, and which other users can read, without regard to the presence of any extra permission bits (for example the executable bit). This will match a file which has mode 0777, for example.
find . -perm /222
Search for files which are writable by somebody (their owner, or their group, or anybody else).
find . -perm /220find . -perm /u+w,g+wfind . -perm /u=w,g=w
All three of these commands do the same thing, but the first one uses the octal representation of the file mode, and the other two use the symbolic form. These commands all search for files which are writable by either their owner or their group. The files don't have to be writable by both the owner and group to be matched; either will do.
find . -perm -220 find . -perm -g+w,u+w
Both these commands do the same thing; search for files which are writable by both their owner and their group.
find . -perm -444 -perm /222 ! -perm /111find . -perm -a+r -perm /a+w ! -perm /a+x
These two commands both search for files that are readable for everybody (-perm -444 or -perm -a+r), have at least on write bit set (-perm /222 or -perm /a+w) but are not executable for anybody (! -perm /111 and ! -perm /a+x respectively)
Compare two drives to see if all files are identical:
find / -path /proc -prune -o -path /new-disk -prune -o -xtype f -exec cmp {} /new-disk{} \
find / -path /proc -prune -o -path /new-disk -prune -o -xtype f -exec cmp {} /new-disk{} \
The -print action lists the names of files separated by a newline. But it is common to pipe the output of find into xargs, which uses a space to separate file names. This can lead to a problem if any found files contain spaces in their names, as the output doesn't use any quoting. In such cases, when the output of find contains a file name such as foo bar and is piped into another command, that command sees two file names, not one file name containing a space. Even without using xargs you could have a problem if the file name contains a newline character.
In such cases you can specify the action -print0 instead. This lists the found files separated not with a newline but with a null (or NUL) character, which is not a legal character in Unix or Linux file names. Of course the command that reads the output of find must be able to handle such a list of file names. Many commands commonly used with find (such as tar or cpio) have special options to read in file names separated with NULs instead of spaces.
we can use shell-style wildcards in the -name search argument:
find . -name foo\*bar
This will search from the current directory down for foo*bar (that is, any filename that begins with foo and ends with bar). Note that wildcards in the name argument must be quoted so the shell doesn't expand them before passing them to find. Also, unlike regular shell wildcards, these will match leading periods in filenames. (For example find -name \*.txt.)
You can search for other criteria beside the name. Also you can list multiple search criteria. When you have multiple criteria any found files must match all listed criteria. That is, there is an implied Boolean AND operator between the listed search criteria. find also allows OR and NOT Boolean operators, as well as grouping, to combine search criteria in powerful ways (not shown here.)
Using the -printf action instead of the default -print is useful to control the output format better than you can with ls or dir. You can use find with -printf to produce output that can easily be parsed by other utilities or imported into spreadsheets or databases. See the man page for the dozens of possibilities with the -printf action. (In fact find with -printf is more versatile than ls and is the preferred tool for forensic examiners even on Windows systems, to list file information.) For example the following displays non-hidden (no leading dot) files in the current directory only (no subdirectories), with an custom output format:
find . -maxdepth 1 -name '[!.]*' -printf 'Name: %16f Size: %6s\n'
-maxdepth is a Gnu extension. On a modern, POSIX version of find you could use this:
find . -path './*' -prune ...
On any version of find you can use this more complex (but portable) code:
find . ! -name . -prune ...
which says to prune (don't descend into) any directories except ..
Note that -maxdepth 1 will include . unless you also specify -mindepth 1. A portable way to include . is:
find . \( -name . -o -prune \) ...
[This information posted by Stephane Chazelas, on 3/10/09 in newsgroup comp.unix.shell.]
As a system administrator you can use find to locate suspicious files (e.g., world writable files, files with no valid owner and/or group, SetUID files, files with unusual permissions, sizes, names, or dates). Here's a final more complex example (which I saved as a shell script):
find / -noleaf -wholename '/proc' -prune \
-o -wholename '/sys' -prune \
-o -wholename '/dev' -prune \
-o -wholename '/windows-C-Drive' -prune \
-o -perm -2 ! -type l ! -type s \
! \( -type d -perm -1000 \) -print
This says to seach the whole system, skipping the directories /proc, /sys, /dev, and /windows-C-Drive (presumably a Windows partition on a dual-booted computer). The Gnu -noleaf option tells find not to assume all remaining mounted filesystems are Unix file systems (you might have a mounted CD for instance). The -o is the Boolean OR operator, and ! is the Boolean NOT operator (applies to the following criteria).
So these criteria say to locate files that are world writable (-perm -2, same as -o=w) and NOT symlinks (! -type l) and NOT sockets (! -type s) and NOT directories with the stickytext) bit set (! \( -type d -perm -1000 \)). (Symlinks, sockets and directories with the sticky bit set are often world-writable and generally not suspicious.) (or
A common request is a way to find all the hard links to some file. Using ls -li file will tell you how many hard links the file has, and the inode number. You can locate all pathnames to this file with:
So these criteria say to locate files that are world writable (-perm -2, same as -o=w) and NOT symlinks (! -type l) and NOT sockets (! -type s) and NOT directories with the stickytext) bit set (! \( -type d -perm -1000 \)). (Symlinks, sockets and directories with the sticky bit set are often world-writable and generally not suspicious.) (or
A common request is a way to find all the hard links to some file. Using ls -li file will tell you how many hard links the file has, and the inode number. You can locate all pathnames to this file with:
find mount-point -xdev -inum inode-number
Since hard links are restricted to a single filesystem, you need to search that whole filesystem so you start the search at the filesystem's mount point. (This is likely to be either /home or /-xdev options tells find to not search any other filesystems. for files in your home directory.) The (While most Unix and all Linux systems have a find command that supports the -inumncheck utility instead that could be used for this.) criterion, this isn't POSIX standard. Older Unix systems provided the
Using -exec Efficiently:
The -exec option to find is great, but since it runs the command listed for every found file it isn't very efficient. On a large system this makes a difference! One solution is to combine find with xargs as discussed above:
find whatever... | xargs command
However this approach has two limitations. Firstly not all commands accept the list of files at the end of the command. A good example is cp:
find . -name \*.txt | xargs cp /tmp # This won't work!
(Note the Gnu version of cp has a non-POSIX option -t for this, and xargs has options to handle this too.)
Secondly filenames may contain spaces or newlines, which would confuse the command used with xargs. (Again Gnu tools have options for that, find ... -print0 |xargs -0 ....)
Secondly filenames may contain spaces or newlines, which would confuse the command used with xargs. (Again Gnu tools have options for that, find ... -print0 |xargs -0 ....)
There are POSIX (but non-obvious) solutions to both problems. An alternate form of -exec ends with a plus-sign, not a semi-colon. This form collects the filenames into groups or sets, and runs the command once per set. (This is exactly what xargs does, to prevent argument lists from becoming too long for the system to handle.) In this form the {} argument expands to the set of filenames. For example:
find / -name core -exec /bin/rm -f '{}' +
This form of -exec can be combined with a shell feature to solve the other problem (names with spaces). The POSIX shell allows us to use:
sh -c 'command-line' [ command-name [ args... ] ]
(We don't usually care about the command-name, so X, dummy, or inline cmd is often used.) Here's an example of efficiently copying found files to /tmp, in a POSIX-compliant way
find . -name '*.txt' -type f \
-exec sh -c 'exec cp -f "$@" /tmp' find-copy {} +
If find doesn't locate any matching files, it produces no output.
The above example said to search the whole system, by specifying the root directory (/) to search. If you don't run this command as root, find will display a error message for each directory on which you don't have read permission. This can be a lot of messages, and the matching files that are found may scroll right off your screen. A good way to deal with this problem is to redirect the error messages so you don't have to see them at all:
The above example said to search the whole system, by specifying the root directory (/) to search. If you don't run this command as root, find will display a error message for each directory on which you don't have read permission. This can be a lot of messages, and the matching files that are found may scroll right off your screen. A good way to deal with this problem is to redirect the error messages so you don't have to see them at all:
find / -name foo 2>/dev/null
We can specify as many places to search as you wish:
find /tmp /var/tmp . $HOME -name foo
The -print action lists the names of files separated by a newline. But it is common to pipe the output of find into xargs, which uses a space to separate file names. This can lead to a problem if any found files contain spaces in their names, as the output doesn't use any quoting. In such cases, when the output of find contains a file name such as foo bar and is piped into another command, that command sees two file names, not one file name containing a space. Even without using xargs you could have a problem if the file name contains a newline character.
In such cases you can specify the action -print0 instead. This lists the found files separated not with a newline but with a null (or NUL) character, which is not a legal character in Unix or Linux file names. Of course the command that reads the output of find must be able to handle such a list of file names. Many commands commonly used with find (such as tar or cpio) have special options to read in file names separated with NULs instead of spaces.
In such cases you can specify the action -print0 instead. This lists the found files separated not with a newline but with a null (or NUL) character, which is not a legal character in Unix or Linux file names. Of course the command that reads the output of find must be able to handle such a list of file names. Many commands commonly used with find (such as tar or cpio) have special options to read in file names separated with NULs instead of spaces.
we can use shell-style wildcards in the -name search argument:
find . -name foo\*bar
This will search from the current directory down for foo*bar (that is, any filename that begins with foo and ends with bar). Note that wildcards in the name argument must be quoted so the shell doesn't expand them before passing them to find. Also, unlike regular shell wildcards, these will match leading periods in filenames. (For example find -name \*.txt.)
we can search for other criteria beside the name. Also you can list multiple search criteria. When you have multiple criteria any found files must match all listed criteria. That is, there is an implied Boolean AND operator between the listed search criteria. find also allows OR and NOT Boolean operators, as well as grouping, to combine search criteria in powerful ways (not shown here.)
find / -name core -exec /bin/rm -f '{}' \; # same thing
find / -name core -delete # same if using Gnu find
(The last two forms run the rm command once per file, and are not as efficient as the first form. However the first form is safer if rewritten to use -print0.)
Another common use is to locate all files owned by a given user (-user username). This is useful when deleting user accounts.
we can also find files with various permissions set. -perm /permissions means to find files with any of the specified permissions on, -perm -permissions means to find files with all of the specified permissions on, and -perm permissions means to find files with exactlypermissions. Permissions can be specified either symbolically (preferred) or with an octal number. The following will locate files that are writeable by others (including symlinks, which should be writeable by all):
find . -perm -o=w
(Using -perm is more complex than this example shows. You should check both the POSIX documentation for find (which explains how the symbolic modes work) and the Gnu find man page (which describes the Gnu extensions).
When using find to locate files for backups, it often pays to use the -depth option (really a criterion that is always true), which forces the output to be depth-first—that is, files first and then the directories containing them. This helps when the directories have restrictive permissions, and restoring the directory first could prevent the files from restoring at all (and would change the time stamp on the directory in any case). Normally, find returns the directory first, before any of the files in that directory. This is useful when using the -prune action to prevent find from examining any files you want to ignore:
find / -name /dev -prune | xargs tar ...
Using the -printf action instead of the default -print is useful to control the output format better than you can with ls or dir. You can use find with -printf to produce output that can easily be parsed by other utilities or imported into spreadsheets or databases. See the man page for the dozens of possibilities with the -printf action. (In fact find with -printf is more versatile than ls and is the preferred tool for forensic examiners even on Windows systems, to list file information.) For example the following displays non-hidden (no leading dot) files in the current directory only (no subdirectories), with an custom output format:
find . -maxdepth 1 -name '[!.]*' -printf 'Name: %16f Size: %6s\n'
-maxdepth is a Gnu extension. On a modern, POSIX version of find you could use this:
find . -path './*' -prune ...
On any version of find you can use this more complex (but portable) code:
find . ! -name . -prune ...
which says to prune (don't descend into) any directories except ..
Note that -maxdepth 1 will include . unless you also specify -mindepth 1. A portable way to include . is:
Note that -maxdepth 1 will include . unless you also specify -mindepth 1. A portable way to include . is:
find . \( -name . -o -prune \) ...
[This information posted by Stephane Chazelas, on 3/10/09 in newsgroup comp.unix.shell.]
As a system administrator you can use find to locate suspicious files (e.g., world writable files, files with no valid owner and/or group, SetUID files, files with unusual permissions, sizes, names, or dates). Here's a final more complex example (which I saved as a shell script):
find / -noleaf -wholename '/proc' -prune \
-o -wholename '/sys' -prune \
-o -wholename '/dev' -prune \
-o -wholename '/windows-C-Drive' -prune \
-o -perm -2 ! -type l ! -type s \
! \( -type d -perm -1000 \) -print
This says to seach the whole system, skipping the directories /proc, /sys, /dev, and /windows-C-Drive (presumably a Windows partition on a dual-booted computer). The Gnu -noleaf option tells find not to assume all remaining mounted filesystems are Unix file systems (you might have a mounted CD for instance). The -o is the Boolean OR operator, and ! is the Boolean NOT operator (applies to the following criteria).
So these criteria say to locate files that are world writable (-perm -2, same as -o=w) and NOT symlinks (! -type l) and NOT sockets (! -type s) and NOT directories with the stickytext) bit set (! \( -type d -perm -1000 \)). (Symlinks, sockets and directories with the sticky bit set are often world-writable and generally not suspicious.) (or
So these criteria say to locate files that are world writable (-perm -2, same as -o=w) and NOT symlinks (! -type l) and NOT sockets (! -type s) and NOT directories with the stickytext) bit set (! \( -type d -perm -1000 \)). (Symlinks, sockets and directories with the sticky bit set are often world-writable and generally not suspicious.) (or
A common request is a way to find all the hard links to some file. Using ls -li file will tell you how many hard links the file has, and the inode number. You can locate all pathnames to this file with:
find mount-point -xdev -inum inode-number
Since hard links are restricted to a single filesystem, you need to search that whole filesystem so you start the search at the filesystem's mount point. (This is likely to be either /home or /-xdev options tells find to not search any other filesystems. for files in your home directory.) The (While most Unix and all Linux systems have a find command that supports the -inumncheck utility instead that could be used for this.) criterion, this isn't POSIX standard. Older Unix systems provided the
Using -exec Efficiently:
The -exec option to find is great, but since it runs the command listed for every found file it isn't very efficient. On a large system this makes a difference! One solution is to combine find with xargs as discussed :
find whatever... | xargs command
However this approach has two limitations. Firstly not all commands accept the list of files at the end of the command. A good example is cp:
find . -name \*.txt | xargs cp /tmp # This won't work!
(Note the Gnu version of cp has a non-POSIX option -t for this, and xargs has options to handle this too.)
Secondly filenames may contain spaces or newlines, which would confuse the command used with xargs. (Again Gnu tools have options for that, find ... -print0 |xargs -0 ....)
There are POSIX (but non-obvious) solutions to both problems. An alternate form of -exec ends with a plus-sign, not a semi-colon. This form collects the filenames into groups or sets, and runs the command once per set. (This is exactly what xargs does, to prevent argument lists from becoming too long for the system to handle.) In this form the {} argument expands to the set of filenames. For example:
find / -name core -exec /bin/rm -f '{}' +
This form of -exec can be combined with a shell feature to solve the other problem (names with spaces). The POSIX shell allows us to use:
sh -c 'command-line' [ command-name [ args... ] ]
(We don't usually care about the command-name, so X, dummy, or inline cmd is often used.) Here's an example of efficiently copying found files to /tmp, in a POSIX-compliant way (Posted on comp.unix.shell netnews newsgroup on Oct. 28 2007 by Stephane CHAZELAS):
find . -name '*.txt' -type f \ -exec sh -c 'exec cp -f "$@" /tmp' find-copy {} +
Common Gotcha:
Common Gotcha:
If the given expression to find does not contain any of the action primaries -exec, -ok, or -print, the given expression is effectively replaced by:
find \( expression \) -print
The implied parenthesis can cause unexpected results. For example, consider these two similar commands:
$ find -name tmp -prune -o -name \*.txt
./bin/data/secret.txt
./tmp
./missingEOL.txt
./public_html/graphics/README.txt
./datafile2.txt
./datafile.txt
$ find -name tmp -prune -o -name \*.txt -print
./bin/data/secret.txt
./missingEOL.txt
./public_html/graphics/README.txt
./datafile2.txt
./datafile.txt
The lack of an action in the first command means it is equivalent to:
find . \( -name tmp -prune -o -name \*.txt \) -print
This causes tmp to be included in the output. However for the second find command the normal rules of Boolean operator precedence apply, so the pruned directory does not appear in the output.
finder-keepers.
In it's simplest use the find command searches for files in the current directory and its subdirectories:
$ find .
./tp1301.txt
./up1301.txt
./tp1302.txt
./up1302.txt
./Up1303.txt
./misc/uploads
./misc/uploads/patch12_13.diff
As always, the dot indicates the current directory. Here find has listed all files found in the current directory and its subdirectories.
If we only want to find files with 'up' at the start of their name, we use the '-name' argument.
If we only want to find files with 'up' at the start of their name, we use the '-name' argument.
So the following would be used:
$ find . -name up\*
./up1301.txt
./up1302.txt
./misc/uploads
find defaults to being case sensitive. If we want the find utility to locate the file 'Up1303.txt' we could either do 'find -name Up\*' or use the iname argument instead of the name argument.
The wildcard character is escaped with a slash so BASH sends a literal asterisk to the find utility as an argument instead of performing filename expansion and passing any number of files in as arguments.
This 'gotcha' is important. Be aware of the characters which the shell attaches special meaning to.
Now we know there are files that should have their names in lowercase we can utilise find to get a list of files with names that aren't:
$ find -iname up\* -not -name up\*
Smooth Operator
find supports boolean algebra with the -and, -or and -not arguments. These are abbreviated as -a, -o and ! (which in bash must be escaped as \!) respectively. The and operator is mentioned here for completeness. Its presence is implied:
$ find . -iname david\*gray\*ogg -type f > david_gray.m3u
These operators are processed in the following order:
Parentheses Use parentheses to force the order in which the operators are evaluated. -not Invert the result of the tested expression. -and E.g. ex1 -and ex2; the second expression isn't checked if the first evaluated to true -or E.g. ex1 -or ex2; as with -AND, the second expression isn't checked if the first evaluated to true ',' This is the list operator where unlike the '-AND' and '-OR' operators both expressions are evaluated. Read the '2 into 1 does go' section for more information. |
The example in the Smooth Operator boxout creates an m3u playlist listing all ogg files that start 'David Gray' (and all case-permutations)
$ find . -iname david\ gray\*ogg -type f > david_gray.m3u
This will find any files called, in one way or the other, "david gray....ogg".
This is semantically equivalent to:
This is semantically equivalent to:
$ find . -iname david\ gray\*ogg -and -type f > david_gray.m3u
It's equivalent to:
$ find . -iname "david gray*ogg" -and -type f > david_gray.m3u
What if the ogg files themselves mightn't have the artists name in them and are in some subdirectory of one called 'David Gray', how do we find them?
$ find . -ipath \*david\ gray\*ogg -type f > david_gray.m3u
The expression starts with a wildcard because its possible there's more than one subdirectory named 'david gray' that might really be nothing more than symlinks for categorisations.
Here's another example, we list the contents of the humour directory (one line per file) and do a case-insensitive search for .mp3 files with 'yoda' in the name of the file:
$ ls humour -1
Weird Al - Yoda.mp3
welcome_to_the_internet_helpdesk.mp3
werid al - livin' la vida yoda.mp3
$ find -ipath \*humour\*yoda\* -type f
./humour/Weird Al - Yoda.mp3
./humour/werid al - livin' la vida yoda.mp3
2 into 1 does go
As implied in the Smooth Operator boxout, it's possible to have one invocation of find perform more than one task.
To compile two lists, one containing the names of all .php files and the other the names of all .js files use:
To compile two lists, one containing the names of all .php files and the other the names of all .js files use:
$ find ~ -type f \( -name \*.php -fprint php_files ,
-name \*.js -fprint javascript_files \)
Pruning
Suppose you have a playlist file listing all David Gray .ogg files but there are a few albums you don't want included.
You can prevent those albums from going into the playlist by using the -prune action which works by attempting to match the names of directories against the given expression.
This example excludes the Flesh and Lost Songs albums :
You can prevent those albums from going into the playlist by using the -prune action which works by attempting to match the names of directories against the given expression.
This example excludes the Flesh and Lost Songs albums :
$ find \( -path ./mp3/David_Gray/Flesh\* -o -path
"./mp3/David_Gray/Lost Songs" \* \) -prune -o -ipath \*david\ gray\*
The first thing you'll notice here is the parentheses are escaped out so BASH doesn't misinterpret them. Notice using -prune takes the form "don't look for these, look for these other ones instead". ie:
$ find (-path <don't want this> -o -path <don't want this#2>)
\-prune -o -path <global expression for what I do want>
It might take a bit longer to invoke find to use the -prune action: decide exactly what you want to do first. I find using the -prune action saves me time I can use on other tasks.
Fussy Fozzy!
There's a host of other expressions and criteria that can be used with find.
Here is a brief rundown on the ones you'll most likely want to use:
| |
-nouser
|
file is owned by someone no longer listed in /etc/passwd
|
-nogroup
|
the group the file belongs to is no longer listed in /etc/groups
|
-owner <username>
|
file is owned by specified user.
|
We'll delve into using these, and others, later on.
Print me the way you want me, baby!
Changing the output information
If you want more than just the names of the files displayed, find's -printf action lets you have just about any type of information displayed. Looking at the man page there is a startling array of options.
These are used the most:
| |
%p
|
filename, including name(s) of directory the file is in
|
%m
|
permissions of file, displayed in octal.
|
%f
|
displays the filename, no directory names are included
|
%g
|
name of the group the file belongs to.
|
%h
|
display name of directory file is in, filename isn't included.
|
%u
|
username of the owner of the file
|
As an example:
$ find . -name \*.ogg -printf %f\\n
generates a list of the filenames of all .ogg files in and under the current directory.
The 'double backslash n' is important; '\n' indicates the start of a new line. The single backslash needs to be escaped by another one so the shell doesn't take it as one of its own.
The 'double backslash n' is important; '\n' indicates the start of a new line. The single backslash needs to be escaped by another one so the shell doesn't take it as one of its own.
Where to output information?
find has a set of actions that tell it to write the information to any file you wish. These are the -fprint, -fprint0 and -fprintf actions. Thus
$ find . -iname david\ gray\*ogg -type f -fprint david_gray.m3u
is more efficient than
$ find . -iname david\ gray\*ogg -type f > david_gray.m3u
Execute!
File is an excellent tool for generating reports on basic information regarding files, but what if you want more than just reports? You could just pipe the output to some other utility:
$ find ~/oggs/ -iname \*.mp3 | xargs rm
This isn't all that efficient though.
It is much better to use the -exec action:
It is much better to use the -exec action:
$ find ~/oggs/ -iname \*.mp3 -exec rm {} \;
It mightn't read as well, but it does mean the files are immediately deleted once found.
'{}' is a placeholder for the name of the file that has been found and as we want BASH to ignore the semicolon and pass it verbatim to find we have to escape it.
'{}' is a placeholder for the name of the file that has been found and as we want BASH to ignore the semicolon and pass it verbatim to find we have to escape it.
To be cautious, the -ok action can be used instead of -exec. The -ok action means you'll be asked for confirmation before the command is executed.
There are many ways these can be used in 'real life' situations:
If you are locked out from the default Mozilla profile, this will unlock you:
If you are locked out from the default Mozilla profile, this will unlock you:
$ find ~/.mozilla -name lock -exec rm {} \;
To compress .log files on an individual basis:
$ find . -name \*.log -exec bzip {} \;
Give user ken ownership of files that aren't owned by any current user:
$ find . -nouser -exec chown ken {} \;
View all .dat files that are in the current directory with vim. Don't search any subdirectories.
$ vim -R `find . -name \*.dat -maxdepth 1`
Look for directories called CVS which are at least four levels below the current directory:
$ find -mindepth 4 -type d -name CVS
Time waits for no-one
You might want to search for recently created files, or grep through the last 3 days worth of log files.
Find comes into its own here: it can limit the scope of the files found according to timestamps.
Now, suppose you want to see what hidden files in your home directory changed in the last 5 days:
$ find ~ -mtime -5 -name \.\*
If you know something has changed much more recently than that, say in the last 14 minutes, and want to know what it was there's the mmin argument:
$ find ~ -mmin 14 -name \.\*
Be aware that doing a 'ls' will affect the access time-stamps of the files shown by that action. If you do an ls to see what's in a directory and try the above to see what files were accessed in the last 14 minutes all files will be listed by find.
To locate files that have been modified since some arbitrary date use this little trick:
$ touch -d "13 may 2001 17:54:19" date_marker
$ find . -newer date_marker
To find files created before that date, use the cnewer and negation conditions:
$ find . \! -cnewer date_marker
To find a file which was modified yesterday, but less than 24 hours ago:
$ find . -daystart -atime 1 -maxdepth
The -daystart argument means the day starts at the actual beginning of the day, not 24 hours ago.
This argument has meaning for the -amin, -atime, -cmin, ctime, -mmin and -mtime options.
Finding files of a specific size
A file of character (bytes)
To locate files that have a certain amount of characters present then you can't go far wrong with
# find files with exactly 1000 characters
$ find . -size 1000c
#find files containing between 600 to 700 characters, inclusive.
$ find . -size +599c -and -size -701c
'Characters' is a misnomer: 'c' is find's shorthand for bytes; thus this will only work for ASCII text not Unicode. Consulting the man page we see
c = bytes
w = 2 byte words
k = kilobytes
b = 512-byte blocks
Thus we can use find to list files of a certain size:
c = bytes
w = 2 byte words
k = kilobytes
b = 512-byte blocks
Thus we can use find to list files of a certain size:
$ find /usr/bin -size 48k
Empty files
You can find empty files with $ find . -size 0c
Using the -empty argument is more efficient.
To delete empty files in the current directory:
Using the -empty argument is more efficient.
To delete empty files in the current directory:
$ find . -empty -maxdepth 1 -exec rm {} \;
Users & Groupies
Users
To locate files belonging to a certain user:
# find /etc -type f \! -user root -exec ls -l {} \;
-rw------- 1 lp sys 19731 2002-08-23 15:04 /etc/cups/cupsd.conf
-rw------- 1 lp sys 97 2002-07-26 23:38 /etc/cups/printers.conf
A subset of that same information, without having the cost of an exec:
root@ttyp0[etc]# find /etc -type f \! -user root \
-printf "%h/%f %u\\n"
/etc/cups/cupsd.conf lp
/etc/cups/printers.conf lp
If you know the uid and not the username then use the -uid argument:
$ find /usr/local/htdocs/www.linux.ie/ -uid 401
-nouser means there is no user in the /etc/passwd file for the files in question.
Groupies
find can locate files that belong to a specific group - or not, depending on how you use it.
This is especially suited to tracking down files that should belong to the www group but don't:
$ find /www/ilug/htdocs/ -type f \! -group www
The -nogroup argument means there is no group in the /etc/group file for the files in question.
This may arise if a group is removed from the /etc/group file sometime after it's been used.
To search for files by the numerical group ID use the -gid argument:
$ find -gid 100
Permissions
If you've ever had one or more shell scripts not work because their execute bits weren't set and want to sort things out for once and for all, then you should like this little example:
knoppix@ttyp1[bin]$ ls -l ~/bin/
total 8
-rwxr-xr-x 1 knoppix knoppix 21 2004-01-20 21:42 wl
-rw-r--r-- 1 knoppix knoppix 21 2004-01-20 21:47 ww
knoppix@ttyp1[bin]$ find ~/bin/ -maxdepth 1 -perm 644 -type f \
-not -name .\*
/home/knoppix/bin/ww
Find locates the file that isn't set to execute, as we can see from the output of ls.
Types of files
The '-type' argument obviously specifies what type of file find is to go looking for (remember in Linux absolutely everything is represented as some type of file).
So far I've been using '-type f' which means search for normal files.
If we want to locate directories with '_of_' in their name we'd use:
So far I've been using '-type f' which means search for normal files.
If we want to locate directories with '_of_' in their name we'd use:
$ find . -type d -name '*_of_*'
The list generated by this won't include symbolic links to directories.
To get a list including directories and symbolic links:
To get a list including directories and symbolic links:
$ find . \( -type d -or -type l \) -name '*_of_*'
For a complete list of types check the man page.
Regular expressions
Thus far we've been using casual wildcards to specify certain groups of files. Find also support regular expressions, so we can use more advanced criteria with regards to locating files. The matching expression must apply to the entire path:
ken@gemmell:/home/library/code$ find . -regex '.*/mp[0-4].*'
./library/sql/mp3_genre_types.sql
The -regex test has a case insensitive counterpart, -iregex.
There is a little gotcha with using regular expressions: You must allow for the full path of the files found, even if find is to search the current directory:
There is a little gotcha with using regular expressions: You must allow for the full path of the files found, even if find is to search the current directory:
$ cd /usr/share/doc/samba-doc/htmldocs/using_samba
$ find . -regex './ch0[1-2]_0[1-3].*'
./ch01_01.html
./ch01_02.html
./ch02_01.html
./ch02_02.html
./ch02_03.html
Limiting by filesytem
As an experiment, get a MS formatted floppy disk and mount it as root:
$ su -
# mount /floppy
# mount
/dev/sda2 on / type ext2 (rw,errors=remount-ro)
proc on /proc type proc (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
/dev/fd0 on /floppy type msdos (rw,noexec,nosuid,nodev)
Now try
$ find / -fstype msdos -maxdepth 1
You should see only /floppy listed.
To get the reverse of this, ie a listing of directories that are not on msdos file-systems, use
To get the reverse of this, ie a listing of directories that are not on msdos file-systems, use
$ find / -maxdepth 1 \( -fstype msdos \) -prune -or -print
find . -name "cookie" -print Start looking in the current directory.
find ~sigmund -name "cookie" -print Start looking in sigmund's home directory.
find /etc -name "cookie" -print Start looking in the /etc directory.
If you can't remember the exact name of the file you're after, but you have some sort of clue about it (for example, it has the word cow in it), you can use wildcards to search:
find . -name "cow*" -print Look for files beginning with cow.
find . -name "*cow" -print Look for files ending with cow.
find . -name "*cow*" -print Look for files with cow anywhere within them.
Note that locate is usually much faster than find, since it has the answers in cache, while find needs to read all the files under the given directory.
Find the passwd file under root and two levels down. (i.e root — level 1, and two sub-directories — level 2 and 3 )
# find / -maxdepth 3 -name passwd
./usr/bin/passwd
./etc/pam.d/passwd
./etc/passwd
Find the password file between sub-directory level 2 and 4.
# find -mindepth 3 -maxdepth 5 -name passwd
./usr/bin/passwd
./etc/pam.d/passwd
4. Executing Commands on the Files Found by the Find Command.
In the example below, the find command calculates the md5sum of all the files with the name MyCProgram.c (ignoring case). {} is replaced by the current file name.
# find -iname "MyCProgram.c" -exec md5sum {} \;
d41d8cd98f00b204e9800998ecf8427e ./mycprogram.c
d41d8cd98f00b204e9800998ecf8427e ./backup/mycprogram.c
d41d8cd98f00b204e9800998ecf8427e ./backup/MyCProgram.c
d41d8cd98f00b204e9800998ecf8427e ./MyCProgram.c
5. Inverting the match.
Shows the files or directories whose name are not MyCProgram.c .Since the maxdepth is 1, this will look only under current directory.
# find -maxdepth 1 -not -iname "MyCProgram.c"
.
./MybashProgram.sh
./create_sample_files.sh
./backup
./Program.c
6. Finding Files by its inode Number.
Every file has an unique inode number, using that we can identify that file. Create two files with similar name. i.e one file with a space at the end.
# touch "test-file-name" # touch "test-file-name " [Note: There is a space at the end]
# ls -1 test*
test-file-name
test-file-name
From the ls output, you cannot identify which file has the space at the end. Using option -i, you can view the inode number of the file, which will be different for these two files.
# ls -i1 test*
16187429 test-file-name
16187430 test-file-name
You can specify inode number on a find command as shown below. In this example, find command renames a file using the inode number.
# find -inum 16187430 -exec mv {} new-test-file-name \;
# ls -i1 *test*
16187430 new-test-file-name
16187429 test-file-name
# touch "test-file-name" # touch "test-file-name " [Note: There is a space at the end]
# ls -1 test*
test-file-name
test-file-name
From the ls output, you cannot identify which file has the space at the end. Using option -i, you can view the inode number of the file, which will be different for these two files.
# ls -i1 test*
16187429 test-file-name
16187430 test-file-name
You can specify inode number on a find command as shown below. In this example, find command renames a file using the inode number.
# find -inum 16187430 -exec mv {} new-test-file-name \;
# ls -i1 *test*
16187430 new-test-file-name
16187429 test-file-name
You can use this technique when you want to do some operation with the files which are named poorly as shown in the example below. For example, the file with name — file?.txt has a special character in it. If you try to execute “rm file?.txt”, all the following three files will get removed. So, follow the steps below to delete only the “file?.txt” file.
# ls
file1.txt file2.txt file?.txt
Find the inode numbers of each file.
# ls -i1
804178 file1.txt
804179 file2.txt
804180 file?.txt
Use the inode number to remove the file that had special character in it as shown below.
# find -inum 804180 -exec rm {} \;
# ls
file1.txt file2.txt
[Note: The file with name "file?.txt" is now removed]
7. Find file based on the File-Permissions
Following operations are possible.
- Find files that match exact permission
- Check whether the given permission matches, irrespective of other permission bits
- Search by giving octal / symbolic representation
For this example, let us assume that the directory contains the following files. Please note that the file-permissions on these files are different.
# ls -l
total 0
-rwxrwxrwx 1 root root 0 2009-02-19 20:31 all_for_all
-rw-r--r-- 1 root root 0 2009-02-19 20:30 everybody_read
---------- 1 root root 0 2009-02-19 20:31 no_for_all
-rw------- 1 root root 0 2009-02-19 20:29 ordinary_file
-rw-r----- 1 root root 0 2009-02-19 20:27 others_can_also_read
----r----- 1 root root 0 2009-02-19 20:27 others_can_only_read
Find files which has read permission to group. Use the following command to find all files that are readable by the world in your home directory, irrespective of other permissions for that file.
# find . -perm -g=r -type f -exec ls -l {} \;
Find files which has read permission to group. Use the following command to find all files that are readable by the world in your home directory, irrespective of other permissions for that file.
# find . -perm -g=r -type f -exec ls -l {} \;
-rw-r--r-- 1 root root 0 2009-02-19 20:30 ./everybody_read
-rwxrwxrwx 1 root root 0 2009-02-19 20:31 ./all_for_all
----r----- 1 root root 0 2009-02-19 20:27 ./others_can_only_read
-rw-r----- 1 root root 0 2009-02-19 20:27 ./others_can_also_read
7. Find file based on the File-Permissions
Following operations are possible.
- Find files that match exact permission
- Check whether the given permission matches, irrespective of other permission bits
- Search by giving octal / symbolic representation
For this example, let us assume that the directory contains the following files. Please note that the file-permissions on these files are different.
# ls -l
total 0
-rwxrwxrwx 1 root root 0 2009-02-19 20:31 all_for_all
-rw-r--r-- 1 root root 0 2009-02-19 20:30 everybody_read
---------- 1 root root 0 2009-02-19 20:31 no_for_all
-rw------- 1 root root 0 2009-02-19 20:29 ordinary_file
-rw-r----- 1 root root 0 2009-02-19 20:27 others_can_also_read
----r----- 1 root root 0 2009-02-19 20:27 others_can_only_read
Find files which has read permission to group. Use the following command to find all files that are readable by the world in your home directory, irrespective of other permissions for that file.
# find . -perm -g=r -type f -exec ls -l {} \;
-rw-r--r-- 1 root root 0 2009-02-19 20:30 ./everybody_read
-rwxrwxrwx 1 root root 0 2009-02-19 20:31 ./all_for_all
----r----- 1 root root 0 2009-02-19 20:27 ./others_can_only_read
-rw-r----- 1 root root 0 2009-02-19 20:27 ./others_can_also_read
Find files which has read permission only to group.
# find . -perm g=r -type f -exec ls -l {} \;
# find . -perm g=r -type f -exec ls -l {} \;
----r----- 1 root root 0 2009-02-19 20:27 ./others_can_only_read
Find files which has read permission only to group [ search by octal ]
# find . -perm 040 -type f -exec ls -l {} \;
Find files which has read permission only to group [ search by octal ]
# find . -perm 040 -type f -exec ls -l {} \;
----r----- 1 root root 0 2009-02-19 20:27 ./others_can_only_read
8. Find all empty files (zero byte file) in your home directory and it’s subdirectory
Most files of the following command output will be lock-files and place holders created by other applications.
# find ~ -empty
List all the empty files only in your home directory.
# find . -maxdepth 1 -empty
List only the non-hidden empty files only in the current directory.
# find . -maxdepth 1 -empty -not -name ".*"
# find ~ -empty
List all the empty files only in your home directory.
# find . -maxdepth 1 -empty
List only the non-hidden empty files only in the current directory.
# find . -maxdepth 1 -empty -not -name ".*"
9. Finding the Top 5 Big Files
The following command will display the top 5 largest file in the current directory and it’s subdirectory. This may take a while to execute depending on the total number of files the command has to process.
# find . -type f -exec ls -s {} \; | sort -n -r | head -5
10. Finding the Top 5 Small Files
Technique is same as finding the bigger files, but the only difference the sort is ascending order.
# find . -type f -exec ls -s {} \; | sort -n | head -5
In the above command, most probably you will get to see only the ZERO byte files ( empty files ). So, you can use the following command to list the smaller files other than the ZERO byte files.
# find . -not -empty -type f -exec ls -s {} \; | sort -n | head -5
12. Find files by comparing with the modification time of other file.
Show files which are modified after the specified file. The following find command displays all the files that are created/modified after ordinary_file.
# ls -lrt
# ls -lrt
total 0
-rw-r----- 1 root root 0 2009-02-19 20:27 others_can_also_read
----r----- 1 root root 0 2009-02-19 20:27 others_can_only_read
-rw------- 1 root root 0 2009-02-19 20:29 ordinary_file
-rw-r--r-- 1 root root 0 2009-02-19 20:30 everybody_read
-rwxrwxrwx 1 root root 0 2009-02-19 20:31 all_for_all
---------- 1 root root 0 2009-02-19 20:31 no_for_all
# find -newer ordinary_file
.
./everybody_read ./all_for_all ./no_for_all
13. Find Files by Size
Using the -size option you can find files by size.
Find files bigger than the given size
# find ~ -size +100M
Find files smaller than the given size
# find ~ -size -100M
Find files bigger than the given size
# find ~ -size +100M
Find files smaller than the given size
# find ~ -size -100M
Find files that matches the exact given size
# find ~ -size 100M
# find ~ -size 100M
14. Create Alias for Frequent Find Operations
If you find some thing as pretty useful, then you can make it as an alias. And execute it whenever you want.
Remove the files named a.out frequently.
# alias rmao="find . -iname a.out -exec rm {} \;" # rmao
Remove the core files generated by c program.
# alias rmc="find . -iname core -exec rm {} \;" # rmc
Remove the files named a.out frequently.
# alias rmao="find . -iname a.out -exec rm {} \;" # rmao
Remove the core files generated by c program.
# alias rmc="find . -iname core -exec rm {} \;" # rmc
15. Remove big archive files using find command
The following command removes *.zip files that are over 100M.
# find / -type f -name *.zip -size +100M -exec rm -i {} \;"
# find / -type f -name *.zip -size +100M -exec rm -i {} \;"
Remove all *.tar file that are over 100M using the alias rm100m (Remove 100M). Use the similar concepts and create alias like rm1g, rm2g, rm5g to remove file size greater than 1G, 2G and 5G respectively.
# alias rm100m="find / -type f -name *.tar -size +100M -exec rm -i {} \;"
# alias rm1g="find / -type f -name *.tar -size +1G -exec rm -i {} \;"
# alias rm2g="find / -type f -name *.tar -size +2G -exec rm -i {} \;"
# alias rm5g="find / -type f -name *.tar -size +5G -exec rm -i {} \;"
# rm100m
# rm1g
# rm2g
# rm5g
Finding files based on the time it is accessed, modified or changed, finding
files comparatively, performing operation on found files etc.
Find Files Based on Access / Modification / Change Time
You can find files based on following three file time attribute.
- Access time of the file. Access time gets updated when the file accessed.
- Modification time of the file. Modification time gets updated when the file content modified.
- Change time of the file. Change time gets updated when the inode data changes.
In the following examples, the difference between the min option and the time option is the argument.
- min argument treats its argument as minutes. For example, min 60 = 60 minutes (1 hour).
- time argument treats its argument as 24 hours. For example, time 2 = 2*24 hours (2 days).
- While doing the 24 hours calculation, the fractional parts are ignored so 25 hours is taken as 24 hours, and 47 hours is also taken as 24 hours, only 48 hours is taken as 48 hours. To get more clarity refer the -atime section of the find command man page.
Example 1: Find files whose content got updated within last 1 hour
To find the files based up on the content modification time, the option -mmin, and -mtime is used. Following is the definition of mmin and mtime from man page.
- -mmin n File’s data was last modified n minutes ago.
- -mtime n File’s data was last modified n*24 hours ago.
Following example will find files in the current directory and sub-directories, whose content got updated within last 1 hour (60 minutes)
# find . -mmin -60
In the same way, following example finds all the files (under root file system /) that got updated within the last 24 hours (1 day).
# find / -mtime -1
# find / -mtime -1
Example 2: Find files which got accessed before 1 hour
To find the files based up on the file access time, the option -amin, and -atime is used. Following is the definition of amin and atime from find man page.
- -amin n File was last accessed n minutes ago
- -atime n File was last accessed n*24 hours ago
Following example will find files in the current directory and sub-directories, which got accessed within last 1 hour (60 minutes)
# find -amin -60
In the same way, following example finds all the files (under root file system /) that got accessed within the last 24 hours (1 day).
# find / -atime -1
Example 3: Find files which got changed exactly before 1 hour
To find the files based up on the file inode change time, the option -cmin, and -ctime is used. Following is the definition of cmin and ctime from find man page.
- -cmin n File’s status was last changed n minutes ago.
- -ctime n File’s status was last changed n*24 hours ago.
Example 4: Restricting the find output only to files. (Display only files as find command results)
The above find command’s will also show the directories because directories gets accessed when the file inside it gets accessed. But if you want only the files to be displayed then give -type f in the find command as
The following find command displays files that are accessed in the last 30 minutes.
# find /etc/sysconfig -amin -30
The following find command displays files that are accessed in the last 30 minutes.
# find /etc/sysconfig -amin -30
.
./console
./network-scripts
./i18n
./rhn ./rhn/clientCaps.d
./networking
./networking/profiles
./networking/profiles/default
./networking/profiles/default/resolv.conf
./networking/profiles/default/hosts
./networking/devices
./apm-scripts
[Note: The above output contains both files and directories]
# find /etc/sysconfig -amin -30 -type f
./i18n
./networking/profiles/default/resolv.conf
./networking/profiles/default/hosts
[Note: The above output contains only files]
Example 5: Restricting the search only to unhidden files. (Do not display hidden files in find output)
When we don’t want the hidden files to be listed in the find output, we can use the following regex.
The below find displays the files which are modified in the last 15 minutes. And it lists only the unhidden files. i.e hidden files that starts with a . (period) are not displayed in the find output.
The below find displays the files which are modified in the last 15 minutes. And it lists only the unhidden files. i.e hidden files that starts with a . (period) are not displayed in the find output.
# find . -mmin -15 \( ! -regex ".*/\..*" \)
Finding Files Comparatively Using Find Command
Human mind can remember things better by reference such as, i want to find files which i edited after editing the file “test”. You can find files by referring to the other files modification as like the following.
Example 6: Find files which are modified after modification of a particular FILE
Syntax: find -newer FILE
Following example displays all the files which are modified after the /etc/passwd files was modified. This is helpful, if you want to track all the activities you’ve done after adding a new user.
# find -newer /etc/passwd
Example 7: Find files which are accessed after modification of a specific FILE
Syntax: find -anewer FILE
Following example displays all the files which are accessed after modifying /etc/hosts. If you remember adding an entry to the /etc/hosts and would like to see all the files that you’ve accessed since then, use the following command.
# find -anewer /etc/hosts
Example 8: Find files whose status got changed after the modification of a specific FILE.
Syntax: find -cnewer FILE
Following example displays all the files whose status got changed after modifying the /etc/fstab. If you remember adding a mount point in the /etc/fstab and would like to know all the files who status got changed since then, use the following command.
find -cnewer /etc/fstab
Perform Any Operation on Files Found From Find Command
We have looked at many different ways of finding files using find command in this article and also in our previous article. If you are not familiar in finding files in different ways, i strongly recommend you to read the part 1.
This section explains about how to do different operation on the files from the find command. i.e how to manipulate the files returned by the find command output.
We can specify any operation on the files found from find command.
This section explains about how to do different operation on the files from the find command. i.e how to manipulate the files returned by the find command output.
We can specify any operation on the files found from find command.
find <CONDITION to Find files> -exec <OPERATION> \;
The OPERATION can be anything such as:
- rm command to remove the files found by find command.
- mv command to rename the files found.
- ls -l command to get details of the find command output files.
- md5sum on find command output files
- wc command to count the total number of words on find command output files.
- Execute any Unix shell command on find command output files.
- or Execute your own custom shell script / command on find command output files.
Example 9: ls -l in find command output. Long list the files which are edited within the last 1 hour.
# find -mmin -60
./cron
./secure
# find -mmin -60 -exec ls -l {} \;
-rw------- 1 root root 1028 Jun 21 15:01 ./cron
-rw------- 1 root root 831752 Jun 21 15:42 ./secure
Example 10: Searching Only in the Current Filesystem
System administrators would want to search in the root file system, but not in the other mounted partitions. When you have multiple partitions mounted, and if you want to search in /. You can do the following.
Following command will search for *.log files starting from /. i.e If you have multiple partitions mounted under / (root), the following command will search all those mounted partitions.
Following command will search for *.log files starting from /. i.e If you have multiple partitions mounted under / (root), the following command will search all those mounted partitions.
# find / -name "*.log"
This will search for the file only in the current file system. Following is the xdev definition from find man page:
- -xdev Don’t descend directories on other filesystems.
Following command will search for *.log files starting from / (root) and only in the current file system. i.e If you have multiple partitions mounted under / (root), the following command will NOT search all those mounted partitions.
# find / -xdev -name "*.log"
Example 11: Using more than one { } in same command
Manual says only one instance of the {} is possible. But you can use more than one {} in the same command as shown below.
# find -name "*.txt" cp {} {}.bkup \;
Using this {} in the same command is possible but using it in different command it is not possible, say you want to rename the files as following, which will not give the expected result.
find -name "*.txt" -exec mv {} `basename {} .htm`.html \;
# find -name "*.txt" cp {} {}.bkup \;
Using this {} in the same command is possible but using it in different command it is not possible, say you want to rename the files as following, which will not give the expected result.
find -name "*.txt" -exec mv {} `basename {} .htm`.html \;
Example 12: Using { } in more than one instance.
You can simulate it by writing a shell script as shown below.
# mv "$1" "`basename "$1" .htm`.html"
These double quotes are to handle spaces in file name. And then call that shell script from the
find command as shown below.
find -name "*.html" -exec ./mv.sh '{}' \; So for any reason if you want the same file name to be used more than once then writing the simple shell script and passing the file names as argument is the simplest way to do it.
# mv "$1" "`basename "$1" .htm`.html"
These double quotes are to handle spaces in file name. And then call that shell script from the
find command as shown below.
find -name "*.html" -exec ./mv.sh '{}' \; So for any reason if you want the same file name to be used more than once then writing the simple shell script and passing the file names as argument is the simplest way to do it.
Example 13: Redirecting errors to /dev/null
Redirecting the errors is not a good practice. An experienced user understands the importance of getting the error printed on terminal and fix it.
Particularly in find command redirecting the errors is not a good practice. But if you don’t want to see the errors and would like to redirect it to null do it as shown below.
Particularly in find command redirecting the errors is not a good practice. But if you don’t want to see the errors and would like to redirect it to null do it as shown below.
find -name "*.txt" 2>>/dev/null
Sometimes this may be helpful. For example, if you are trying to find all the *.conf file under / (root) from your account, you may get lot of “Permission denied” error message as shown below.
$ find / -name "*.conf"
/sbin/generate-modprobe.conf
find: /tmp/orbit-root: Permission denied
find: /tmp/ssh-gccBMp5019: Permission denied
find: /tmp/keyring-5iqiGo: Permission denied
find: /var/log/httpd: Permission denied
find: /var/log/ppp: Permission denied
/boot/grub/grub.conf
find: /var/log/audit: Permission denied
find: /var/log/squid: Permission denied
find: /var/log/samba: Permission denied
find: /var/cache/alchemist/printconf.rpm/wm: Permission denied
[Note: There are two valid *.conf files burned in the "Permission denied" messages
So, if you want to just view the real output of the find command and not the “Permission denied” error message you can redirect the error message to /dev/null as shown below.
$ find / -name "*.conf" 2>>/dev/null
$ find / -name "*.conf" 2>>/dev/null
/sbin/generate-modprobe.conf
/boot/grub/grub.conf
[Note: All the "Permission denied" messages are not displayed]
Example 14: Substitute space with underscore in the file name.
Audio files you download from internet mostly come with the spaces in it. But having space in the file name is not so good for Linux kind of systems. You can use the find and rename command combination as shown below to rename the files, by substituting the space with underscore.
The following replaces space in all the *.mp3 files with _
$ find . -type f -iname “*.mp3″ -exec rename “s/ /_/g” {} \;
The following replaces space in all the *.mp3 files with _
$ find . -type f -iname “*.mp3″ -exec rename “s/ /_/g” {} \;
Example 15: Executing two find commands at the same time
As shown in the examples of the find command in its manual page, the following is the syntax which can be used to execute two commands in single traversal.
The following find command example, traverse the filesystem just once, listing setuid files and directories into /root/suid.txt and large files into /root/big.txt.
# find / \( -perm -4000 -fprintf /root/suid.txt '%#m %u %p\n' \) , \
The following find command example, traverse the filesystem just once, listing setuid files and directories into /root/suid.txt and large files into /root/big.txt.
# find / \( -perm -4000 -fprintf /root/suid.txt '%#m %u %p\n' \) , \
\( -size +100M -fprintf /root/big.txt '%-10s %p\n' \)
find . -type f -mtime -1 -print | xargs pr -n
Find any files that have a modified time of 1 day and print the contents of each of those files.
find Members/ -type f -print0 | xargs -0 grep "examplestring"
In the above example the find command finds all files in the Members directory each file that is found is then searched using grep for the text "examplestring".
find and xargs do go very well together: find to locate what you're looking for, and xargs to run the same command on each of the things found.
Traditionally, an advantage to xargs was its ability to handle long command lines before failing, unlike some other commands. This command:
rm `find tmp -maxdepth 1 -name '*.mp3'`
is intended to remove all tmp/*.mp3 files (and ignore any subdirectories), but can fail with an "Argument list too long" message. This exact equivalent:
find tmp -maxdepth 1 -name '*.mp3' -maxdepth 1 | xargs rm
does exactly the same thing but will avoid the problem by batching arguments up. More modern kernels (since 2.6.23) shouldn't have this issue, but it's wise to make your scripts as portable as possible; and the xargs version is also easier on the eye.You can also manually batch arguments if needed, using the -n option.
find tmp -maxdepth 1 -name '*.mp3' -maxdepth 1 | xargs -n1 rm
will pass one argument at a time to rm. This is also useful if you're using the -p option as you can confirm one file at a time rather than all at once.
Filenames containing whitespace can also cause problems; xargsand find can deal with this, using GNU extensions to both to break on the null character rather than on whitespace:
find tmp -maxdepth 1 -name *.mp3 -print0 | xargs -0 rm
You must use these options either on both find and xargs or on neither, or you'll get odd results.Another common use of xargs with find is to combine it withgrep. For example,
find . -name '*.pl' | xargs grep -L '^use strict'
will search all the *.pl files in the current directory and subdirectories, and print the names of any that don't have a line starting with 'use strict'. Enforce good practice in your scripting!
find and xargs do go very well together: find to locate what you're looking for, and xargs to run the same command on each of the things found.
Traditionally, an advantage to xargs was its ability to handle long command lines before failing, unlike some other commands. This command:
find and xargs do go very well together: find to locate what you're looking for, and xargs to run the same command on each of the things found.
Traditionally, an advantage to xargs was its ability to handle long command lines before failing, unlike some other commands. This command:
rm `find tmp -maxdepth 1 -name '*.mp3'`
is intended to remove all tmp/*.mp3 files (and ignore any subdirectories), but can fail with an "Argument list too long" message. This exact equivalent:
find tmp -maxdepth 1 -name '*.mp3' -maxdepth 1 | xargs rm
does exactly the same thing but will avoid the problem by batching arguments up. More modern kernels (since 2.6.23) shouldn't have this issue, but it's wise to make your scripts as portable as possible; and the xargs version is also easier on the eye.
You can also manually batch arguments if needed, using the -n option.
find tmp -maxdepth 1 -name '*.mp3' -maxdepth 1 | xargs -n1 rm
will pass one argument at a time to rm. This is also useful if you're using the -p option as you can confirm one file at a time rather than all at once.
Filenames containing whitespace can also cause problems; xargsand find can deal with this, using GNU extensions to both to break on the null character rather than on whitespace:
find tmp -maxdepth 1 -name *.mp3 -print0 | xargs -0 rm
You must use these options either on both find and xargs or on neither, or you'll get odd results.Another common use of xargs with find is to combine it withgrep. For example,
find . -name '*.pl' | xargs grep -L '^use strict'
will search all the *.pl files in the current directory and subdirectories, and print the names of any that don't have a line starting with 'use strict'. Enforce good practice in your scripting!
Moving on from find: it can be useful to pipe the contents of a file into xargs as input. So,
xargs -t -n2 diff < diff-files
would take the arguments listed in the file diff-files in groups of 2 and run diff on them. So if the diff-files file consisted of:
sample1 alternate1 sample2 alternate2
then xargs would run:
diff sample1 alternate1 diff sample2 alternate2
This can be a quick way of comparing large numbers of files. (Use -p instead of -t to get a pause after each diff as well as an echo of the command.)
You can also use a listings file and xargs to concatenate the contents of those files:
xargs cat < list-of-files > files-contents
(generate list-of-files using xargs as well!
find . -maxdepth 1 -name '*.tex' | xargs echo > list-of-files
would get all your LaTeX source files in the current directory into one list, ready to be stuck together.)
You can use xargs if you need to rename lots of files (e.g. datestamping). This command will rename each file in the current directory from filename.txt to20080815-filename.txt:
ls | xargs -I {} mv {} 20080815-{}
This works because {} is a placeholder meaning "the current argument". (You can use xxx or yyy or any other string instead of {} if you want, as well, and it'll do exactly the same thing.) -I implies -n1, because you want to act on each file individually.
Or you might want to move all the files in directory 1 into directory 2:
ls dir1 | xargs -I {} -t mv dir1/{} dir1/{}
I've concentrated here on using xargs to manipulate files in various ways, but you can use the same tricks for other commands. For example, if you have a file containing a list of IP addresses,
cat iplist | xargs -n1 nmap -sV
would run nmap on each IP address at a time. Play around with it a bit and see what you can do!
The simplistic approach using find is
find /whereveryouwantostart -exec grep whatever {} dev/null \;
That's not necessarily very efficient. Using xargs can help
find . | xargs grep whatever
But it also has bugs if the filenames could have "-" at their beginning. Fixing that can be a little nasty.
You may not want to grep binary files:
find . -type f -print|xargs file|grep -i text|cut -fl -d: | xargs grep whatever
That's pretty awful, but it's what you have to get into if you have special cases. Special cases are what makes this question more difficult. If you have a small number of files and subdirs to search, the simple approach may work fine for you. If not, you have to get more creative.
Moving on from find: it can be useful to pipe the contents of a file into xargs as input. So,
xargs -t -n2 diff < diff-files
would take the arguments listed in the file diff-files in groups of 2 and run diff on them. So if the diff-files file consisted of:
sample1 alternate1
sample2 alternate2
then xargs would run:
diff sample1 alternate1
diff sample2 alternate2
This can be a quick way of comparing large numbers of files. (Use -p instead of -t to get a pause after each diff as well as an echo of the command.)
You can also use a listings file and xargs to concatenate the contents of those files:
xargs cat < list-of-files > files-contents
(generate list-of-files using xargs as well!
find . -maxdepth 1 -name '*.tex' | xargs echo > list-of-files
would get all your LaTeX source files in the current directory into one list, ready to be stuck together.)
i've been trying to figure this weird error but I cannot seem to know why. I am using below find command:
find . \( ! -name . -prune \) -type f -mtime +365 -print
The above code returns no file because no files are really more then 365 days old. However, when I use xargs, its returning all the files on the directory.
Code:
find . \( ! -name . -prune \) -type f -mtime +365 | xargs ls
What seems to be the problem with this. I need to use xargs to delete the files find by the find command later on. (when i used exec its working perfectly fine). It will go through a lot of files so I prefer using xargs than exec on this.
|
xargs just do an ls command without any arguments, something like:
echo "" | xargs ls
Try it with -exec:
find . \( ! -name . -prune \) -type f -mtime +365 -exec ls {} \;
|
find ... | xargs -I{} ls {}
|
find . -type f -mtime +365 -delete
find . -type f -mtime +365 -printf '"%p"\n' | xargs -i rm -fv {}
|
Two points.
|
Find is able to execute one or more commands for each file it has found with the -exec option. Unfortunately, one cannot simply enter the command. You need to remember two syntactic tricks:
- The command that you want to execute need to contain a special macro argument {}, which will be replaced by the matched filename on each invocation of -exec predicate.
- You need to specify \; (or ';' ) at the end of the command. (If the \ is left out, the shell will interpret the ; as the end of the find command.) . For example, the following two commands are equivalent:
find . -name "*rc.conf" -exec chmod o+r {} \;
find . -name "*rc.conf" -exec chmod o+r '{} ;'
find . -name "*rc.conf" -exec chmod o+r '{} ;'
In case {} macro parameter is the last item in the command then it should be a space between the {} and the \;. For example:
find . -type d -exec ls -ld {} \;
If you attempt to make changes that involve system directories it is better to do it in two stages. first create a file with the list of changes using find and verify that it is accurate. Then use xargs with option -p (see below) to process this file.
In case of deletion of the file GNU find has option -delete which is safer then "-exec /bin/rm {} \;".
For example find / -name core -delete
For example find / -name core -delete
There is classic problem of using rm in case you have filenames with spaces, for example files that migrated to Unix filesystem from Windows where, unfortunately, using spaces in filenames is a common practice. For example you might need to delete all documents that ends with "doc copy":
find /mnt/zip -name "*doc copy"
There are three ways to solve this problem:
- Use option -delete (GNU find only)
- Rename all files replacing spaces with underscore.
- Use quote over argument in rm command
- find /mnt/zip -name "*doc copy" -exec rm "{}" \;
- Use xarg with option -0 and find command with option -print0 (see discussion below, in section devoted to xargs):
find /mnt/zip -name "*prefs copy" -print0 | xargs -0 rm
Again it is better to experiment first to see if everything is right if you deal with important files. Five minutes of testing can save five or more hours of desperate attempts to recover accidentally deleted files.
Here are examples of "good practices" of using find. We will use chmod as the base of examples. Many people do not think about commands like chmod or chown as particularly dangerous, but applied to root filesystem they can be pretty devastating. Please note that we first get to the target directory using cd and only then are using find command with "." (dot) argument. This avoids such unpleased situation as typing "/ etc" instead of "/etc" or "/etc" instead of etc (the intention was to get to local etc directory but string "/etc" is hardwired in sysadmin brains and this slip costs many sysadmins tremendous pain):
Test command
· find . -type f -ls
Final command
· find . -type f -exec chmod 500 {} ';'
The command bellow search in the current directory and all sub directories and change permissions of each file as specified. Here an additional danger is connected with being in a wring directory.
Test command
find . -name "*rc.conf" -ls
Final command
find . -name "*rc.conf" -exec chmod o+r {} \;
This command will search in the current directory and all sub directories. All files named *rc.conf will be processed by the chmod -o+r command. The argument {} is a macro that expands to each found file. The \; argument indicates the exec argument has ended. You can use ';' instead:
find . -name "*rc.conf" -exec chmod o+r {} ';'
The end results of this command is all *rc.conf files have read bit set in "other" permissions.
The find command is commonly used to remove core files that are more than a few 24-hour periods (days) old. These core files are copies of the actual memory image of a running program when the program dies unexpectedly. They can be huge, so occasionally trimming them is wise:
Test command
find . -name core -ctime +4 -ls
Final command
find . -name core -ctime +4 -exec /bin/rm -f {} \;
For grep the /dev/null argument can by used to show the name of the file before the text that is found. Without it, only the text found is printed. An equivalent mechanism in GNU find is to use the "-H" or "--with-filename" option to grep:
find /tmp -exec grep "search string" {} /dev/null \; -print
An alternative to -exec option is piping output into xargs command which we will discuss in the next section.
One of the biggest limitations of the -exec option (or predicate with the side effect to be more correct) is that it can only run the specified command on one file at a time. The xargs command solves this problem by enabling users to run a single command on many files at one time. In general, it is much faster to run one command on many files, because this cuts down on the number of invocations of particular command/utility.
Note: Print0 with print list of filenames with null character (\0) instead of whitespace as the output delimiter between pathnames found. This is a safer option if files can contain blanks or other special characters if you use find withxargs (the -0 argument is needed in xargs.).
|
For example often one needs to find files containing a specific pattern in multiple directories one can use an exec option in find
find . -type f -exec grep -iH '/bin/ksh' {} \;
But there is more elegant and more Unix-like way of accomplishing the same task using xargs and pipes. You can use the xargs to read the output of find and build a pipeline that invokes grep. This way, grep is called only four or five times even though it might check through 200 or 300 files. By default, xargs always appends the list of filenames to the end of the specified command, so using it with grep and most other Unix command is pretty natural:
find . -type f -print | xargs grep -il 'bin/ksh'
This gave the same output a lot faster (-l option in grep prints only the names of files with matching lines, separated by NEWLINE characters. Does not repeat the names of files when the pattern is found more than once.)
Also the xargs is used with grep it will be getting multiple filenames, it will automatically include the filename of any file that contains a match. Still option -H for grep (or addition /dev/null to the list of files) is recommended as the last "chunk" of filenames can contain a single file.
When used in combination, find, grep, and xargs are a potent team to help find files lost or misplaced anywhere in the UNIX file system. I encourage you to experiment further. You can use time to find the difference in speed with -exec option vs xarg in the following way:
time find /usr/src -name "*.html" -exec grep -H "foo" {} ';' | wc -l
time find /usr/src -name "*.html" | xargs grep -l "foo" | wc -l
xargs works considerably faster. The difference becomes even greater when more complex commands are run and the list of files is longer.
Two other useful options for xargs are the -p option, which makes xargs interactive, and the -n args option, which makes xargs run the specified command with only N number of arguments. Option -0 is often used with -print0
This combination is useful if you need to operate on filenames with spaces. If you add option -print0 to find command and option -0 to xargs command, you can avoid the danger to processing wrong file(s) xargs:
find /mnt/zip -name "*prefs copy" -print0 | xargs -0 rm
Using option -p you can provide manual confirmation of each action. The reason is that xargs runs the specified command on the filenames from its standard input, so interactive commands such as cp -i, mv -i, and rm -i don't work right.
So when you run the command first time you can use this option as a safety valve. After several operations with confirmation you can cancel it and run without option -p. The -p option solves that problem. In the preceding example, the -p option would have made the command safe because I could answer yes or no to each file. Thus, the command I typed was the following:
find /mnt/zip -name "*prefs copy" -print0 | xargs -p rm
Many users frequently ask why xargs should be used when shell command substitution archives the same results. Take a look at this example:
grep foo ´find /usr/src/linux -name "*.html"´
The drawback with commands such as this is that if the set of files returned by find is longer than the system's command-line length limit, the command will fail.
The xargs approach gets around this problem because xargs runs the command as many times as is required, instead of just once.
But ability of xargs to use multiple argument can be a source of the problems too. For example
find . -type f -name "*.java" | xargs tar cvf myfile.tar
Here the attempt is made to create a backup of all java files in the current tree: But if the list length for xargs to invoke the tar command twice or more, it will overwrite previous tar and the resulting archive will contain a fraction of files.
To solve this problem you can use either file (tar can read a list of files from the file using -T option) or "-r" option which tells tar to append to the archive, while option '-c' means "create".
To solve this problem you can use either file (tar can read a list of files from the file using -T option) or "-r" option which tells tar to append to the archive, while option '-c' means "create".
find . -type f -name "*.java" | xargs tar rvf myfile.tar
Always test find statements with “-print” before adding “-exec rm {} \;”.
Command to create a very large tar archive:
find . -type f -name "*.java" | xargs tar rvf myfile.tar
Xargs reads arguments from the standard input, delimited by blanks (which can be protected with double or single quotes or a backslash) or newlines, and executes the command (default is /bin/echo) one or more times with any initial-arguments fol- lowed by arguments read from standard input. Blank lines on the standard input are ignored.
echo * | xargs echo
asciiart.tar.gz assn.tar.gz bin.tgz doc.tar.gz
echo * | xargs -n 1 echo
asciiart.tar.gz
assn.tar.gz
bin.tgz
doc.tar.gz
--replace[=replace-str], -i[replace-str]
Replace occurences of replace-str in the initial arguments
with names read from standard input. Also, unquoted blanks do
not terminate arguments. If replace-str is omitted, it
defaults to "{}" (like for `find -exec'). Implies -x and -l
1.
--max-lines[=max-lines], -l[max-lines]
Use at most max-lines nonblank input lines per command line;
max-lines defaults to 1 if omitted. Trailing blanks cause an
input line to be logically continued on the next input line.
Implies -x.
--max-args=max-args, -n max-args
Use at most max-args arguments per command line. Fewer than
max-args arguments will be used if the size (see the -s
option) is exceeded, unless the -x option is given, in which
case xargs will exit.
-t Enable trace mode. Each generated command
line will be written to standard error just
prior to invocation.
$ echo `jot 3` | xargs -n 1 -i echo {} {}
1 2 3 1 2 3
$ jot 3 | xargs -i -n 1 echo {} {}
{} {} 1
{} {} 2
{} {} 3
$ jot 3 | xargs -i echo {} {}
1 1
2 2
3 3
— Use above, 'ls -1 | xargs -i' !!,
$ ls ind* | xargs -i echo mv {} _{}
mv index.html _index.html
mv index001.html _index001.html
— no -1 will also do!!! <<:Thu 06-24-99:>>
*N*: If you have to use echo, use this (Sun 03-14-99) echo * | xargs -n 1 | xargs -i echo {} {}
alias xargsi="tr '\n' '\0' | xargs -0 -i"
— the same as alias xargsi="xargs -i"
alias xargs1="tr '\n' '\0' | xargs -0 -n 1"
— the same as alias xargsi="xargs -l 1"
$ ls
I mean "no"
a b'c d
$ find . -print0 | xargs -0 -l1 rm
rm: cannot remove `.' or `..'
find . -group 500
find ~/www/ai -name lget-log.txt -print -exec cat {} \;
— Should use parameters at full length
find /etc/ -newer /root/anaconda-ks.cfg
-size N[bckw]
True if the file uses N units of space, rounding up. The units
are 512-byte blocks by default, but they can be changed by adding a
one-character suffix to N:
`b' 512-byte blocks
`c' bytes
`k' kilobytes (1024 bytes)
`w' 2-byte words
find /java/expresso/webapps/expresso/expresso/doc/edg/ /www/html/docs/expresso/javadoc/ -name '*.html'
host:~/ndl>find . -name *.tar -exec echo mv {} `fname {}`.bar \;
mv ./st.tar .bar
host:~/ndl>find . -name *.gz -exec echo mv {} `fname {}`.bar \;
find: paths must precede expression
Usage: find [path...] [expression]
— the same syntax with previous, the only difference is this time many files note that the previous is not working as supposed to be.
find /var/ -path '*/spool' -prune -o -path '*/cpan' -prune -o -path '*/www' -prune -o -type d
host:~/ndl>find . -name *.tar -exec 'echo mv {} `fname {}`.bar \;'
find: missing argument to `-exec'
host:~/ndl>find . -name *.tar '-exec echo mv {} `fname {}`.bar \;'
find: invalid predicate `-exec echo mv {} `fname {}`.bar \;'
host:~/ndl>find . -name *.tar -'exec echo mv {} `fname {}`.bar \;'
find: invalid predicate `-exec echo mv {} `fname {}`.bar \;'
find ~/www/ai -name lget-log.txt -print '-exec echo ; cat {} \;'
find: bad option -exec echo ; cat {} \;
find: path-list predicate-list
iitrc:~/ndl/libwww/libwww-perl-5.41/bin$ find . -name '*.PL' -exec echo mv {} {}.bar \;
mv ./lwp-rget.PL {}.bar
mv ./lwp-download.PL {}.bar
mv ./lwp-mirror.PL {}.bar
mv ./lwp-request.PL {}.bar
- Should use '' when using */?
- Can only use {} once!
- give up trying to use more than one command, or {}
find -exec support
How can I use ` in find -exec?
find -name '*.tgz' -exec echo mv {} `basename {}` \;
The simplest solution is to write a script that does what you want, and invoke that with the -exec option. E.g. create a script named moveit that contains:
#!/bin/sh
mv "$1" "`basename $1`"
and then do:
find -name '*.tgz' -exec moveit {} \;
find ~/temp -name 'b*.tgz' -exec fileh fnh mv basename {} \;
Delete
What is the easiest way to delete all the (say) .o files under all
the sub-directories?
find . -name "*.o" -print | xargs rm -f
Deal with file names that have ' in it
$ find | xargs echo
xargs: unmatched single quote
ls | grep "'" | doeach.pl mv "@'@_@'" "@'@~echo \\@'@_\\@' @b sed \\@'s/'/@@/g\\@'@~@'"
find . -ls | cut -c68- | sed "s/'/\\\\'/g" | xargs touch -c -t 200104150000
# -maxdepth 1
find -print0 | xargs -0 echo
ddate=200104150000
find -print0 | xargs -0 touch -c -t $ddate
find -type d -print0 | xargs -0 touch -c -t $ddate
documented on: 2001.04.13 Fri 03:43:11
get mod
How can I get access mode of a file?
find /etc/passwd -printf %m 444
Find man page for strings, iosteam
whereis is not very helpful.
echo $MANPATH | tr ":" "\n" | doeach.pl find @_ ' -name string*'
find new
So, what should I do, to find files that are modified within 3 days?
It's obscurely noted in the manual, but for numbers:
+n means greater than n
n means exactly n
-n means less than
So you would want:
find path -mtime -3
find new
find -mtime -5 -printf "%TY-%Tm-%Td %TT %p\n" | sort
find read-only files
I can use -perm parameter of find to find writable files but how can I
find read-only files?
Read-only files are files that are not writable, so you first write an expression that matches writable files and then negate it:
find ! \( -perm -200 -o -perm -020 -o -perm -002 \)
find read-only file
find . ! -perm +0222
How to search for a file from is size?
find / -size +102400k
Yeah, that'd work, but for more general use, the -s option to ls might work better. Less to type, anyway. "ls -s | sort -n" will give you a list of all the files in the current directory with size in K prepended, sorted by size with largest last. Add -R to the ls, you'll get a recursive listing.
Copy and preserve hard links
Note that the rsync '-a' (archive mode) does not preserve hard links:
$ ls -li inc*
107256 -rw------- 3 tong tong 10 03-23 10:33 inc.txt
107544 lrwxrwxrwx 1 tong tong 7 03-23 12:13 inc2.txt -> inc.txt
107256 -rw------- 3 tong tong 10 03-23 10:33 inc31.txt
107256 -rw------- 3 tong tong 10 03-23 10:33 inc32.txt
rsync -vua . /tmp/lnk_tst
cd /tmp/lnk_tst
$ ls -li inc*
217084 -rw------- 1 tong tong 10 03-23 10:33 inc.txt
217061 lrwxrwxrwx 1 tong tong 7 06-02 11:47 inc2.txt -> inc.txt
217085 -rw------- 1 tong tong 10 03-23 10:33 inc31.txt
217086 -rw------- 1 tong tong 10 03-23 10:33 inc32.txt
For the rsync command to preserve hard links, use an extra -H:
-H, --hard-links preserve hard links
rm -rf /tmp/lnk_tst
rsync -vuaH . /tmp/lnk_tst
cd /tmp/lnk_tst
$ ls -li inc*
217067 -rw------- 3 tong tong 10 03-23 10:33 inc.txt
216974 lrwxrwxrwx 1 tong tong 7 06-28 16:44 inc2.txt -> inc.txt
217067 -rw------- 3 tong tong 10 03-23 10:33 inc31.txt
217067 -rw------- 3 tong tong 10 03-23 10:33 inc32.txt
$ rsync -v
rsync version 2.6.9 protocol version 29
To copy files while preserving hard links, use tar:
mkdir /tmp/lnk_tst2
tar -cSf - . | tar -xvSpf - -C /tmp/lnk_tst2
cd /tmp/lnk_tst2
$ ls -li inc*
217117 -rw------- 3 tong tong 10 03-23 10:33 inc.txt
217118 lrwxrwxrwx 1 tong tong 7 06-02 11:55 inc2.txt -> inc.txt
217117 -rw------- 3 tong tong 10 03-23 10:33 inc31.txt
217117 -rw------- 3 tong tong 10 03-23 10:33 inc32.txt
$ tar --version
tar (GNU tar) 1.16
Well, actually 'cp -a' preserves hard links as well:
cp -a . /tmp/lnk_tst
$ ls -li /tmp/lnk_tst/inc*
217074 -rw------- 3 tong tong 10 03-23 10:33 /tmp/lnk_tst/inc.txt
217075 lrwxrwxrwx 1 tong tong 7 06-14 15:37 /tmp/lnk_tst/inc2.txt -> inc.txt
217074 -rw------- 3 tong tong 10 03-23 10:33 /tmp/lnk_tst/inc31.txt
217074 -rw------- 3 tong tong 10 03-23 10:33 /tmp/lnk_tst/inc32.txt
Is it possible to find the hard links of the same file?
find . -type f -links +1 -ls | sort -n -k 1
This command line will find all regular files (-type f) that have 2 or more hard links (-links +1) and list them (-ls, format similar to ls -l, except that it includes the inode number in column one). The result is piped to a numeric sort on column one.
xargs command using shell pipes and not able to understand how to control and use command line arguments. For example I'd like to find out all *.c file located in 100s of sub-directories and move them to another directory called ~/old.src. How do I use command line args with xargs to achieve the same?
xargs command is designed to construct argument lists and invoke other utility. xargs reads items from the standard input or pipes, delimited by blanks or newlines, and executes the command one or more times with any initial-arguments followed by items read from standard input. Blank lines on the standard input are ignored.
xargs is more safer and easy to use
xargs functionality can be achived using the backquote feature of shell. But, it offers more options. It can deal with blanks or special characters in file names easily. It is often used with find, grep and other commands.
xargs examples
For example following example will print 1 2 3 4 using xargs (echo command is default)
$ echo 1 2 3 4 | xargs echo
OR
$ echo 1 2 3 4 | xargs
You can force xargs to use at most max-args arguments per command line. For example following will use first two argument per command:
$ echo 1 2 3 4 | xargs -n 2
Find all .bak files in or below the current directory and delete them.
$ find . -name "*.bak" -type f -print | xargs /bin/rm -f
$ echo 1 2 3 4 | xargs echo
OR
$ echo 1 2 3 4 | xargs
You can force xargs to use at most max-args arguments per command line. For example following will use first two argument per command:
$ echo 1 2 3 4 | xargs -n 2
Find all .bak files in or below the current directory and delete them.
$ find . -name "*.bak" -type f -print | xargs /bin/rm -f
{} as the argument list marker
{} is the default argument list marker. You need to use {} this with various command which take more than two arguments at a time. For example mv command need to know the file name. The following will find all .bak files in or below the current directory and move them to ~/.old.files directory:
$ find . -name "*.bak" -print0 | xargs -0 -I {} mv {} ~/old.files
You can rename {} to something else. In the following example {} is renamed as file. This is more readable as compare to previous example:
$ find . -name "*.bak" -print0 | xargs -0 -I file mv file ~/old.files
Where,
$ find . -name "*.bak" -print0 | xargs -0 -I {} mv {} ~/old.files
You can rename {} to something else. In the following example {} is renamed as file. This is more readable as compare to previous example:
$ find . -name "*.bak" -print0 | xargs -0 -I file mv file ~/old.files
Where,
1. -0 If there are blank spaces or characters (including newlines) many commands will not work. This option take cares of file names with blank space.
2. -I Replace occurrences of replace-str in the initial-arguments with names read from standard input. Also, unquoted blanks do not terminate input items; instead the separator is the newline character.
Dealing file names with blank spaces and newline
The following will work incorrectly if there are any filenames containing newlines or spaces (it will find out all .mp3 file located in current directory and play them using mplayer):
$ find . -iname "*.mp3" -print | xargs mplayer
To get rid of this problem use -0 option:
$ find . -iname "*.mp3" -print0 | xargs -0 -I mp3file mplayer mp3file
To find out all *.c file located in 100s of subdirectories and move them to another directory called ~/old.src, use:
$ find /path/to/dir -iname "*.c" -print0 | xargs -0 -I file mv file ~/old.src
$ find . -iname "*.mp3" -print | xargs mplayer
To get rid of this problem use -0 option:
$ find . -iname "*.mp3" -print0 | xargs -0 -I mp3file mplayer mp3file
To find out all *.c file located in 100s of subdirectories and move them to another directory called ~/old.src, use:
$ find /path/to/dir -iname "*.c" -print0 | xargs -0 -I file mv file ~/old.src
Avoiding errors and resource hungry problems with xargs and find combo
To copy all media files to another location called /bakup/iscsi, you can use cp as follows:
$ cp -r -v -p /share/media/mp3/ /backup/iscsi/mp3
However, cp command may fail if an error occurs such as if the number of files is too large for the cp command to handle. xargs in combination with find can handle such operation nicely. xargs is more resource efficient and will not halt with an error:
$ cp -r -v -p /share/media/mp3/ /backup/iscsi/mp3
However, cp command may fail if an error occurs such as if the number of files is too large for the cp command to handle. xargs in combination with find can handle such operation nicely. xargs is more resource efficient and will not halt with an error:
$ find /share/media/mp3/ -type f -name "*.mp3" -print0 | xargs -0 -r -I file cp -v -p file --target-directory=/bakup/iscsi/mp3
Please note that all of the above commands are tested with GNU/xargs version. BSD and UNIX xargs command may not have options such as -r. Please refer to your local xargs man page for further info:
man xargs
Find all files on your system that are world writable. The 0002 denotes a 2 in the "other" field in the file permissions, which is the write bit
find / -perm -0002
Collect files that are not owned by valid users and delete them
find / -nouser -print0 | xargs -0 rm
Clean the images off of your *nix desktop
find ~/Desktop -name "*.jpg" -o -name "*.gif" -o -name "*.png" -print0 | xargs -0 mv --target-directory ~/Pictures** The -print0 option terminates results with a null character instead of the default newline, making it cleaner and less likely to balk in many cases
Correct the permissions on your web directory
find /your/webdir/ -type d -print0 | xargs -0 chmod 755
find /your/webdir -type f | xargs chmod 644
find /your/webdir -type f | xargs chmod 644
Show a list of files in /etc that have been modified since last month
find /etc -mtime -30
Comments
Post a Comment