find是UNIX/Linux命令行工具箱中最棒的工具之一。这个命令对编写shell脚本很有帮助。
find命令的工作方式如下:沿着文件层次结构向下遍历,匹配符合条件的文件,并执行相应的操作。
1. 根据文件名或正则表达式匹配搜索
选项-name的参数指定了文件名所必须匹配的字符串。我们可以将通配符最为参数使用。*.txt能够匹配所有的.txt结尾的文件名。选项-print在终端中打印符合条件(例如-name)的文件名或文件路径。
$find /home/obama -name ”*.txt” -print
有个与-name相似的选项,-iname(忽略大小写)。
$ls
example.txt EXAMPLE.txt file.txt
$find . –iname “example*” –print
./example.txt
./EXAMPLE.txt
如果想匹配多个条件,可以采用OR条件操作:
$ls
new.txt some.jpg text.pdf
$find . \( -name “*.txt” -o -name “*.pdf” \) –print
./text.pdf
./new.txt
\(以及\)用于将-name “*.txt” -o -name “*.pdf”视为一个整体。
选项-path可以使用通配符来匹配文件路径或文件。
-name总是用给定的文件名进行匹配。-path则将文件路径最为一个整体进行匹配。
例如:
$find /home/users -path “*slynux*” -print
This will match files as following paths.
/home/users/list/slynux.txt
/home/users/slynux/eg.css
选项-regex的参数和-path类似,只不过-regex是基于正则表达式来匹配文件路径的。类似的-iregex用于忽略大小写。
2. 反选
find可以用”!”否定参数的含义。例如:
$ls
list.txt new.PY new.txt next.jpg test.py
$find . ! -name “*.txt” -print
.
./next.jpg
./test.py
./new.PY
上面命令用于匹配所有不以.txt结尾的文件名。
3. 基于目录深度的搜索
find默认会遍历所有子目录。可以采用-maxdepth和-mindepth参数。
我们可以用-maxdepth参数指定最大深度。与此相似,可以指定一个最小深度,告诉find应该从此处开始向下查找,-mindepth参数设置最小深度。
$find . –maxdepth 1 -type f -print
只列出当前目录下的普通文件。即使有子目录也不会被打印和遍历。
注意:如果-type放在–maxdepth前面,find会先找出符合-type的所有文件,然后在所有匹配的文件中在找出符合指定深度的那些。但是,如果先指定目录深度,在-type,那么find先找出符合指定深度的文件后,在按-type查找,这才是最有效的搜索顺序。
4. 用文件类型过滤
-type指定要搜索的目标文件类型。
只列出所有目录:
$find . –type d -print
将文件和目录分别列出不是个容易事。不过有find就好办了。只列出普通文件:
$find . –type f -print
只列出符号链接:
$find . –type l -print
文件类型 | 类型参数 |
普通文件 | f |
符号链接 | l |
目录 | d |
字符设备 | c |
块设备 | b |
套接字 | s |
FIFO | p |
5. 根据文件时间进行搜索
UNIX/Linux文件系统中的每一个文件都有三种时间戳(timestamp),如下
l 访问时间(-atime):用户最近一次访问文件的时间
l 修改时间(-mtime):文件内容最后一次被修改的时间
l 变化时间(-ctime):文件元数据(metadata,例如权限或所有权)最后一次改变的时间
在UNIX中并没有所谓的“创建时间”的概念。
-atime、-mtime、-ctime可最为find的时间参数。它们可以整数值给出,单位是天。这些整数值通常还带有-或+:-表示小于,+表示大于,如下所示。
打印出在最近七天内被访问过的所有文件:
$find . –type f -atime -7 -print
打印出恰好在七天前被访问过的所有文件:
$find . –type f -atime 7 -print
打印出访问时间超过七天的所有文件:
$find . –type f -atime +7 -print
-atime、-mtime、-ctime默认单位是“天”。还有一些基于时间的参数是以分钟作为计量单位的。
l -amin
l -mmin
l -cmin
find另一个漂亮的特性是-newer参数。使用这个参数,可以指定一个用于比较时间戳的参考文件,然后找出比参考文件更新的(更长的修改时间)所有文件。
例如,找出比file.txt修改时间更长的所有文件:
$find . –type f -newer file.txt -print
find命令的时间戳操作选项对编写系统备份和维护脚本很有帮助。
6. 按文件大小搜索
$find . –type f -size +2k
#大于2KB的文件
$find . –type f -size -2kb
#小于2KB的文件
$find . –type f -size 2k
#大小等于2KB的文件
出来K之外,还可以用其他单位。
b——块(512字节)
c——字节
w——字(2字节)
k——千字节
M——兆字节
G——吉字节
7. 删除匹配文件
-delete可以用来删除find查找到的匹配文件。
删除当期目录下的所有.swp文件:
$find . –type f -name “*.swp” -delete
8. 按文件权限和所有权查找
文件匹配可以根据文件权限进行。列出具有特定权限的所有文件:
$find . –type f -perm 644 -print
#打印出权限为644的文件
可以用这种方法找出那些没有设置好执行权限的文件。
也可以根据文件的所有权进行搜索。用选项-user USER就能找出某个特定用户有用的文件:
参数USER可以是用户名或UID
例如,打印出用户Obama拥有的所有文件:
$find . –type f -user Obama -print
9. 结合find,执行
find命令可以借助选项-exec与其他命令结合。-exec算是find最强大的特性之一。
将给定目录中的所有C程序文件拼接起来写入单个文件all_c_files.txt。我们可以用find找到所有的C文件,然后结合-exec使用cat命令:
$find . –type f -name “*.c” -exec cat {} \; >all_c_files.txt
-exec之后可以接任何命令,对于任何一个匹配的文件名,{}会被该文件名所替换。例如find找到了两个文件test1.c和test2.c,那么那么cat {}将会执行cat test1.c和cat test2.c。
我们使用>操作符将来自find的数据流重定向到all_c_files.txt文件,没有使用>>(追加)的原因是因为find命令的全部输出只是一个单数据流(stdin),而只有当多个数据流被追加到单个文件时候才有必要使用>>。
无法在-exec参数中直接使用多个命令,它只能接受单个命令,不过我们可以耍个小花招。把多个命令写到一个shell脚本中(例如command.sh),然后子啊-exec中使用:-exec ./command.sh {} \;
10. 让find跳过特定的目录
为了提高性能,在搜索时,需要跳过一些目录,例如git为会为每个目录建立.git目录。因为版本控制目录对我们而言并没有什么用处,所以没有必要去搜索这些目录。
$find ./source_path \( -name “.git” -prune \) -o \( -type f -print \)
这里,\( -name “.git” -prune \)用于排除,\( -type f -print \)
指明了需要执行的操作。这些动作需要放置在第二个语句块中。
11. 删除含有特殊字符的文件
比如删除,文件名的第一个字符是-的文件,使用rm删除的话,将优先会将文件名解释成选项,因为选项也是用-指定,man rm,最后有说到使用:
rm -- -foo或rm - ./-foo
另外这里,我们可以直接删除i节点的方法,删除该文件。
可以使用ls -i与stat查看文件的inode,当然如果特殊字符正好是上述情况,第一个字符是-的话,使用ls与stat手动查看inode,需要./-foo。
找到inode号后使用-inum选项结合-delete,将含特殊字符的文件删除。
$find . –inum INODE_NUM -delete
12. 路径必须在表达式之前(paths must percede expression)
例如当前目录含有a.c、b.c、c.c
$find . -name *.c -print
find: paths must percede expression
出现这种错误的原因,shell先将*.c扩展成a.c b.c c.c
相当于命令变成:
$find . –name a.c b.c c.c -print
这是种错误使用方式。取而代之,可以对*.c加上双引号:
$find . –name “*.c” -print
或者使用\,将\放在*前,避开该通配符。
$find . –name \*.c -print