Difference between revisions of "Linux - find"

From Ever changing code
Jump to navigation Jump to search
 
(53 intermediate revisions by the same user not shown)
Line 1: Line 1:
<font face="Verdana, Arial, Helvetica, sans-serif" size="-1">Files can be found under Linux in many different ways. Using the find tool is one of the best ways to find files. The find tool has a huge number of parameters which can be set so that Linux finds exactly those files that you were searching for. Many users use the find tool with just the basic parameters. They get the results that they were looking for. Unfortunately most of the users don't spend time to learn all about find. If they do, they can make excellent use of this tool and I am sure you would be surprised at the possibilities.<br/><br/>In case you just want to know where a particular file exists on your system, and nothing else is required, then use ''locate'' tool. [http://www.codecoffee.com/tipsforlinux/articles/20.html Article No.20] explains how to use ''locate''.<br/><br/><br/>Here are a few ways to use find<br/><br/>-<br/><br/>'''$ find / -name 'program.c' 2>/dev/null<br/>$ find / -name 'program.c' 2>errors.txt'''</font>
Files can be found under Linux in many different ways, using the find tool is one of the best ways to find files.  


{| border="0" cellpadding="0" cellspacing="0" width="100%"
= <code>locate</code> =
|-
Searches for a filename in a system.
| width="11%" | <div align="right"><font face="Verdana, Arial, Helvetica, sans-serif" size="-1">'''/'''</font></div>
<source lang=bash>
| width="3%" | &nbsp;
updatedb  # update database with all filenames on a filesystem
| width="86%" | <font face="Verdana, Arial, Helvetica, sans-serif" size="-1">Start searching from the root directory (i.e / directory)</font>
locate 'filename*.conf' # search
|-
</source>
| width="11%" | <div align="right"><font face="Verdana, Arial, Helvetica, sans-serif" size="-1">'''-name'''</font></div>
| width="3%" | &nbsp;
| width="86%" | <font face="Verdana, Arial, Helvetica, sans-serif" size="-1">Given search text is the filename rather than any other attribute of a file</font>
|-
| width="11%" | <div align="right"><font face="Verdana, Arial, Helvetica, sans-serif" size="-1">''''program.c''''</font></div>
| width="3%" | &nbsp;
| width="86%" | <font face="Verdana, Arial, Helvetica, sans-serif" size="-1">Search text that we have entered. Always enclose the filename in single quotes.. why to do this is complex.. so simply do so.</font>
|}


<font face="Verdana, Arial, Helvetica, sans-serif" size="-1">'''<font size="-2">Note :</font>'''<font size="-2">2>/dev/null is not related to find tool as such. 2 indicates the error stream in Linux, and /dev/null is the device where anything you send simply disappears. So 2>/dev/null in this case means that while finding for the files, in case any error messages pop up simply send them to /dev/null i.e. simply discard all error messages.<br/><br/>Alternatively you could use 2>error.txt where after the search is completed you would have a file named error.txt in the current directory with all the error messages in it. </font><br/><br/>-</font>
= <code>find</code> =
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]
default path is the current directory; default expression is -print
=== Find a string within a file but not follow symlinks / grep don't follow sym-links ===
find . -type f | xargs grep "string-to-search"


<font face="Verdana, Arial, Helvetica, sans-serif" size="-1">'''$ find /home/david -name 'index*'<br/>$ find /home/david -iname 'index*''''<br/>The 1st command would find files having the letters index as the beginning of the file name. The search would be started in the directory /home/david and carry on within that directory and its subdirectories only.<br/>The 2nd command would search for the same, but the case of the filename wouldn't be considered. So all files starting with any combination of letters in upper and lower case such as INDEX or indEX or index would be returned.<br/><br/>-<br/><br/>'''$ find -name met*'''<br/>The above command would start searching for the files that begin with the letters 'met' within the current directory and the directories that are present within the current directory. Since the directory is not specified as the the second parameter, Linux defaults to using the current directory as the one to start the search in.<br/><br/>-<br/><br/>'''$ find /mp3collection -name '*.mp3' -size -5000k<br/>$ find / -size +10000k'''<br/>The 1st command would find within a directory called /mp3collection, only those mp3 files that have a size less than 5000 Kilobytes ( < 5MB)<br/>The 2nd command would search from the / directory for any file that is larger than 10000k (> 10MB)<br/><br/>-<br/><br/>'''$ find /home/david -amin -10 -name '*.c'<br/>$ find /home/david -atime -2 -name '*.c'<br/>$ find /home/david -mmin -10 -name '*.c'<br/>$ find /home/david -mtime -2 -name '*.c''''<br/><br/>The 1st commmand searches for those files that are present in the directory /home/david and its subdirectoires which end in .c and which have been accessed in the last 10 minutes.<br/>The 2nd command does the same but searches for those files that have been accessed in the last 10 hours.<br/>The 3rd and the 4th commands do the same as the 1st and 2nd commands but they search for modified files rather than accessed files. Only if the contents of the files have been modified, would their names be returned in the search results.<br/><br/>-<br/><br/>'''$ find / -mount -name 'win*''''<br/>This command searches for files starting with the letters 'win' in their filenames. The only difference is that the mounted filesystems would not be searched for this time. This is useful when you have your Windows partitions mounted by default. And a search for 'win' might return many files on those partitions, which you may not be really interested in. This is only one use of -mount parameter.<br/><br/>-</font><br/><br/>'''<font face="Verdana, Arial, Helvetica, sans-serif" size="-1">$ find /mp3-collection -name 'Metallica*' -and -size +10000k<br/>$ find /mp3-collection -size +10000k ! -name "Metallica*"<br/>$ find /mp3-collection -name 'Metallica*' -or -size +10000k</font>'''<br/><font face="Verdana, Arial, Helvetica, sans-serif" size="-1">Boolean operators such as AND, OR and NOT make find an extremely useful tool.<br/>The 1st command searches within the directory /mp3-collection for files that have their names beginning with 'Metallica' and whose size is greater than 10000 kilobytes (> 10 MB).<br/>The 2nd command searches in the same directory as above case but only for files that are greater than 10MB, but they should not have 'Metallica' as the starting of their filenames.<br/>The 3rd command searches in the same directory for files that begin with 'Metallica' in their names or all the files that are greater than 10 MB in size.<br/><br/>-<br/><br/>T</font><font face="Verdana, Arial, Helvetica, sans-serif" size="-1">he '''exec''' option is probably the most important feature of the find tool. The exec command allows you to execute a particular command on the results of the find command. A simple demonstration of this feature is shown below. Its upto your imagination to make maximum use of this feature. Suppose you wanted to see the details of the files (read, write, execute permission, file size, owner etc..) that have been returned as a search result you could do the following<br/><br/>'''$ find / - name 'Metallica*' -exec ls -l {\}\ \;'''<br/><br/>This command would find all the files on your system that begin with the letters 'Metallica' and would then execute the 'ls -l' command on these files. So basically you would be able to see the details of the files that were returned according to your search criteria.<br/><br/>The words following the -exec option is the command that you want to execute i.e. ls -l in this case.<br/>{\}\ is basically an indicator that the filenames returned by the search should be substituted here.<br/>\; is the terminating string, and is required at the end of the command<br/><br/>-<br/><br/>Find has plenty of more options, which I shall discuss in some other article.</font>
=== Find a file with a specific name ===
<source lang="bash">  
find /etc -iname -perm 777 'program.c'
# / -start searching from the root directory, by default it will be recursive search also within sub-directories
# -name - filename search
# -iname -case insensitive sensitive filename search
# -type f -perm 777 -file with specific permissions
</source>
 
=== Searching paths -path ===
To search within a path we can use the <code>-path</code> flag instead of the <code>-name</code> flag. Let’s try to find any files whose path contains the word <code>session</code>.
<source lang="bash">
$ find . -path \*session\*         #find files and directories
./actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb
./actionpack/lib/action_dispatch/request/session.rb
./actionpack/test/dispatch/request/session_test.rb
./actionpack/test/dispatch/session
./actionpack/test/dispatch/session/abstract_store_test.rb
 
$ find . -path \*session\* -type d #finds only directories
./actionpack/lib/action_dispatch/middleware/session
./actionpack/test/dispatch/session
</source>
=== Find files bigger than ===
This command below finds files bigger than 999999B only on / root filesystem, locking up to a single file achieves by using -xdev switch
<source lang="bash">
find / -xdev -size +999999 -exec ls -lhd {} \;
</source>
 
=== Find with time ===
<source lang="bash">
find /home -cmin +10 -name '*.conf' # created  more than 10 minutes ago
find /home -amin -10 -name '*.conf' # accessed less than 10 minutes ago
find /home -mmin  10 -name '*.conf' # modified exactly  10 minutes ago
 
find /home -ctime +2 -name '*.conf' # created  more then 2 days ago
find /home -atime -2 -name '*.conf' # accessed less than 2 days ago
find /home -mtime 2 -name '*.conf' # modified exactly  2 days ago
</source>
 
=== Find with size ===
<source lang="bash">
find /mp3collection -name '*.mp3' -size -5000k # have a size less than 5000 Kilobytes (< 5MB)
find / -size +10000k                          # any file that is larger than 10000k  (> 10MB)
find / -size +10000k                          # any file that is larger than 10M    (> 10MB)
</source>
 
=== Find mounting points ===
$ find / -mount -name 'win*
This command searches for files starting with the letters 'win' in their filenames. The only difference is that the mounted filesystems would not be searched for this time. This is useful when you have your Windows partitions mounted by default. And a search for 'win' might return many files on those partitions, which you may not be really interested in. This is only one use of -mount parameter.
 
=== Boolean operators such as AND, OR and NOT ===
Boolean operators such as AND, OR and NOT make find an extremely useful tool. The 1st command searches within the directory /mp3-collection for files that have their names beginning with 'Metallica' and whose size is greater than 10000 kilobytes (> 10 MB). The 2nd command searches in the same directory as above case but only for files that are greater than 10MB, but they should not have 'Metallica' as the starting of their filenames. The 3rd command searches in the same directory for files that begin with 'Metallica' in their names or all the files that are greater than 10 MB in size.
<source lang="bash">
find /mp3-collection -name 'Metallica*' -and -size +10000k
find /mp3-collection -size +10000k ! -name "Metallica*"
find /mp3-collection -name 'Metallica*' -or -size +10000k
</source>
 
=== Performing operations ===
==== Find and execute command (-exec) ====
The '''exec''' option is probably the most important feature of the find tool. The exec command allows you to execute a particular command on the results of the find command. A simple demonstration of this feature is shown below. Its upto your imagination to make maximum use of this feature. Suppose you wanted to see the details of the files (read, write, execute permission, file size, owner etc..) that have been returned as a search result you could do the following
<source lang="bash">
# find string within find files results
find . -type f -exec grep -l 'string-to-search' {} \;
# find and delete files modified more than 5 days ago
find /home -mtime +5 -exec rm {} \; 
</source>
 
 
The words following the -exec option is the command that you want to execute i.e. ls -l in this case.
*<code>-exec command {} \;</code> curly brackets expand to a single matching file, line by line, a backslash <code>\</code> escapes command termination <code>;</code> can also be used <code>';'</code> so won't need escaping.
==== Find directory's and execute command to change permissions ====
<source lang="bash">
$ find . -type d -exec chmod 0755 {} \;
</source>
 
==== Find and -delete ====
The find tool also has the built-in ability to execute certain commands. The <code>-delete</code> flag will, as its name suggests, delete all files and directories that match the pattern. Here the <code>-print</code> flag is used to show the files that will be deleted.
<source lang="bash">
find ./logs -mtime +14 -type f              -print -delete # delete files older than 14 days
find ./logs            -type f -name \*.log -print -delete # delete files '*.log'
</source>
 
==== Find and -exec | rename ====
<source lang="bash">
sudo apt-get install rename #it's Perl version of rename util
$ find . -iname "*foo*" -print0 | xargs -0 rename "s/<old>/<new>/"
# find files mattching fileglob *foo*
# -print0 :- print space delimited instead line by line
# pass each found file_path to Perl's rename utility to do 's' substitution of pattern '<old>' to '<new>'
# the '/' is here as a delimiter
</source>
 
;xargs
Because Unix filenames can contain blanks and newlines, this default behaviour is often problematic; filenames containing  blanks        and/or newlines  are incorrectly processed by <code>xargs</code>. In these situations it is better to use the <code>-0</code> option, which prevents such        problems. When using this option you will need to ensure that the program which produces the input for <code>xargs</code> also uses a null character as a separator. If that program is GNU <code>find</code> the <code>-print0</code> option does this for you.
 
==== Rename using bash expansion ====
<source lang="bash">
for i in *.ogg; do mv "$i" "${i/Beatles/TheBeatles}"; done
# following expansion is doing replace in string
</source>
 
==== Find and update symlink content ====
This works for both updating a directory in symlink or part of target name.
<source lang="bash">
find . -type l -lname '*old*' -printf '%l\0%p\0' | sed -z 's|old|NEW|;n' | xargs -r0n2 ln -sfT
# -type l :- symlink
# -lname  :- target of symlink matches a pattern
 
vars_app.tf -> ../../../stacks/old/vars_old.tf #before
vars_app.tf -> ../../../stacks/NEW/vars_NEW.tf #after
</source>
 
==== Find a file using grep and sed it ====
<source lang="bash">
$ grep -rl <pattern> * | xargs sed -i 's/<old>/<new>/g'
# -rl :- find and display files matching <pattern>
# pass each file path to sed to do substitution
</source>
 
= <code>ack</code> and <code>ag</code> - grep on steroids =
Tools for searching large code bases. Ack and Ag are 99% feature compatible with each other, so you can use them interchangeably. Ag is significantly faster than Ack, and it has built-in version control system (VCS) awareness.
 
Both Ack and Ag treat the search term as a regular expression by default.
<source lang="bash">
# Install CentOS, Amazon Linux AMI
yum search silver_searcher
sudo yum install -y the_silver_searcher.x86_64
 
# Install Ubuntu
$ sudo apt-get install ag ack
ag LOG #search for 'LOG' string any file in CWD and display matching lines
ag  ^LOG #regular expression search (default)
ag -Q LOG #literal string search
ag LOG /var/log/ #search within given path
# -l -list files that contain matching pattern
# -i -case insensitive search
# -G <file_matching_pattern> -scope search to files names that match a pattern
# --ignore-dir -ignore path(s) in search
</source>
 
 
Old <code>ack-grep</code> tool
<source lang="bash">
sudo apt-get install ack-grep
ack-grep 'Cisco1941'
</source>
 
= References =
*[http://www.codecoffee.com/tipsforlinux/articles/20.html article about Locate]


[[Category:Linux]]
[[Category:Linux]]

Latest revision as of 11:23, 11 June 2021

Files can be found under Linux in many different ways, using the find tool is one of the best ways to find files.

locate

Searches for a filename in a system.

updatedb  # update database with all filenames on a filesystem
locate 'filename*.conf' # search

find

Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

default path is the current directory; default expression is -print

Find a string within a file but not follow symlinks / grep don't follow sym-links

find . -type f | xargs grep "string-to-search"

Find a file with a specific name

find /etc -iname -perm 777 'program.c'
# / -start searching from the root directory, by default it will be recursive search also within sub-directories
# -name - filename search
# -iname -case insensitive sensitive filename search
# -type f -perm 777 -file with specific permissions

Searching paths -path

To search within a path we can use the -path flag instead of the -name flag. Let’s try to find any files whose path contains the word session.

$ find . -path \*session\*         #find files and directories
./actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb
./actionpack/lib/action_dispatch/request/session.rb
./actionpack/test/dispatch/request/session_test.rb
./actionpack/test/dispatch/session
./actionpack/test/dispatch/session/abstract_store_test.rb

$ find . -path \*session\* -type d #finds only directories
./actionpack/lib/action_dispatch/middleware/session
./actionpack/test/dispatch/session

Find files bigger than

This command below finds files bigger than 999999B only on / root filesystem, locking up to a single file achieves by using -xdev switch

find / -xdev -size +999999 -exec ls -lhd {} \;

Find with time

find /home -cmin +10 -name '*.conf' # created  more than 10 minutes ago
find /home -amin -10 -name '*.conf' # accessed less than 10 minutes ago
find /home -mmin  10 -name '*.conf' # modified exactly   10 minutes ago

find /home -ctime +2 -name '*.conf' # created  more then 2 days ago
find /home -atime -2 -name '*.conf' # accessed less than 2 days ago
find /home -mtime  2 -name '*.conf' # modified exactly   2 days ago

Find with size

find /mp3collection -name '*.mp3' -size -5000k # have a size less than 5000 Kilobytes (<  5MB)
find / -size +10000k                           # any file that is larger than 10000k  (> 10MB)
find / -size +10000k                           # any file that is larger than 10M     (> 10MB)

Find mounting points

$ find / -mount -name 'win*

This command searches for files starting with the letters 'win' in their filenames. The only difference is that the mounted filesystems would not be searched for this time. This is useful when you have your Windows partitions mounted by default. And a search for 'win' might return many files on those partitions, which you may not be really interested in. This is only one use of -mount parameter.

Boolean operators such as AND, OR and NOT

Boolean operators such as AND, OR and NOT make find an extremely useful tool. The 1st command searches within the directory /mp3-collection for files that have their names beginning with 'Metallica' and whose size is greater than 10000 kilobytes (> 10 MB). The 2nd command searches in the same directory as above case but only for files that are greater than 10MB, but they should not have 'Metallica' as the starting of their filenames. The 3rd command searches in the same directory for files that begin with 'Metallica' in their names or all the files that are greater than 10 MB in size.

find /mp3-collection -name 'Metallica*' -and -size +10000k
find /mp3-collection -size +10000k ! -name "Metallica*"
find /mp3-collection -name 'Metallica*' -or -size +10000k

Performing operations

Find and execute command (-exec)

The exec option is probably the most important feature of the find tool. The exec command allows you to execute a particular command on the results of the find command. A simple demonstration of this feature is shown below. Its upto your imagination to make maximum use of this feature. Suppose you wanted to see the details of the files (read, write, execute permission, file size, owner etc..) that have been returned as a search result you could do the following

# find string within find files results 
find . -type f -exec grep -l 'string-to-search' {} \;
# find and delete files modified more than 5 days ago
find /home -mtime +5 -exec rm {} \;


The words following the -exec option is the command that you want to execute i.e. ls -l in this case.

  • -exec command {} \; curly brackets expand to a single matching file, line by line, a backslash \ escapes command termination ; can also be used ';' so won't need escaping.

Find directory's and execute command to change permissions

$ find . -type d -exec chmod 0755 {} \;

Find and -delete

The find tool also has the built-in ability to execute certain commands. The -delete flag will, as its name suggests, delete all files and directories that match the pattern. Here the -print flag is used to show the files that will be deleted.

find ./logs -mtime +14 -type f              -print -delete # delete files older than 14 days
find ./logs            -type f -name \*.log -print -delete # delete files '*.log'

Find and -exec | rename

sudo apt-get install rename #it's Perl version of rename util
$ find . -iname "*foo*" -print0 | xargs -0 rename "s/<old>/<new>/"
# find files mattching fileglob *foo*
# -print0 :- print space delimited instead line by line
# pass each found file_path to Perl's rename utility to do 's' substitution of pattern '<old>' to '<new>'
# the '/' is here as a delimiter
xargs

Because Unix filenames can contain blanks and newlines, this default behaviour is often problematic; filenames containing blanks and/or newlines are incorrectly processed by xargs. In these situations it is better to use the -0 option, which prevents such problems. When using this option you will need to ensure that the program which produces the input for xargs also uses a null character as a separator. If that program is GNU find the -print0 option does this for you.

Rename using bash expansion

for i in *.ogg; do mv "$i" "${i/Beatles/TheBeatles}"; done
# following expansion is doing replace in string

Find and update symlink content

This works for both updating a directory in symlink or part of target name.

find . -type l -lname '*old*' -printf '%l\0%p\0' | sed -z 's|old|NEW|;n' | xargs -r0n2 ln -sfT
# -type l :- symlink
# -lname  :- target of symlink matches a pattern

vars_app.tf -> ../../../stacks/old/vars_old.tf #before
vars_app.tf -> ../../../stacks/NEW/vars_NEW.tf #after

Find a file using grep and sed it

$ grep -rl <pattern> * | xargs sed -i 's/<old>/<new>/g'
# -rl :- find and display files matching <pattern>
# pass each file path to sed to do substitution

ack and ag - grep on steroids

Tools for searching large code bases. Ack and Ag are 99% feature compatible with each other, so you can use them interchangeably. Ag is significantly faster than Ack, and it has built-in version control system (VCS) awareness.

Both Ack and Ag treat the search term as a regular expression by default.

# Install CentOS, Amazon Linux AMI
yum search silver_searcher
sudo yum install -y the_silver_searcher.x86_64

# Install Ubuntu
$ sudo apt-get install ag ack
ag LOG #search for 'LOG' string any file in CWD and display matching lines
ag   ^LOG #regular expression search (default)
ag -Q LOG #literal string search
ag LOG /var/log/ #search within given path
# -l -list files that contain matching pattern
# -i -case insensitive search
# -G <file_matching_pattern> -scope search to files names that match a pattern
# --ignore-dir -ignore path(s) in search


Old ack-grep tool

sudo apt-get install ack-grep
ack-grep 'Cisco1941'

References