# Shell find命令 find [starting-point...] [expression] 从starting-point指定的目录开始查找, 例如: find ./ -print 从当前目录开始查找,并打印目录和文件列表 -print 打印出匹配文件的文件名, 并使用 \n 作为分隔文件的定界符。 -print0 使用\0作为分隔文件的定界符。 ## 匹配文件名或类型 *-name* 指定了文件名必须匹配的字符串,可以使用通配符, 例如 ".txt"表示匹配所有以 .txt结尾的文件名 例: `find ./ -name "*.txt" -print` *-iname* 与-name类似,只不过在匹配名字的时候会忽略大小写. *操作符* `\(expr\)` 括号需要使用反斜杠转义 `! expr` 对expr取反,表示否定 `expr1 -a expr2` 两个表达式相与 `expr1 -o expr2` 两个表达式相或 例如: `find ./ \( -name "*.txt" -o -name "*.pdf" \) -print` *-regex* 基于正则表达式来匹配文件. 例如: 查找当前目录下所有的pdf或者txt文件 `find ./ -regex ".*\(\.pdf\|\.sh\)$"` *指定目录深度* find默认会遍历所有的子目录, -maxdepth设置最大深度, -mindepth指定最小深度,表示从指定的深度开始向下查找. 例: `find ./ -mindepth 2 -type 2 -print` 注意: -maxdepth或-mindepth应该作为靠前的参数出现,不然可能会影响find的效率 例如例如 -maxdepth 在 -type 参数之后, 那么find将先找出所有符合type的文件,然后筛选指定的深度范围. *-type* 指定文件类型 ## 根据文件时间查找 Unix/Linux文件系统中的每个文件都有3种时间戳(timestamp) 访问时间(-atime): 用户最近一次访问该文件的时间 修改时间(-mtime): 文件内容最后一次被修改的时间 变化时间(-ctime): 文件元数据(metadata,例如权限或者所有权)最后一次被修改的时间 -atime -mtime -ctime 可以作为find的选项,其后跟整数值,单位是天. 这些整数值还可以带上+或者-, +表示大于,-表示小于 例如: ``` 1. 查找最近7天被访问过的文件 find ./ -type f -atime -7 2. 查找恰好在7天前被访问过的文件 find ./ -type f -atime 7 3. 查找访问时间超过7天的所有文件 find ./ -type f -atime +7 ``` 类似地,也可以使用-mtime -ctime进行查找 以分钟为计量单位的选项: -amin -mmin -cmin -newer 查找比指定文件更新的文件(修改时间大于指定文件) 例: 查找比file.txt修改时间更长的文件 `find ./ -type f -newer file.txt -print` ## 根据文件大小查找 -size ``` 1. 查找大于2k的文件 find . -type f -size +2k 2. 查找小于2k的文件 find . -type f -size -2k 3. 查找等于2k的文件 find . -type f -size 2k ``` 除了k之外,还可以使用其他的单位 b block(1个block可能等于512Byte或者4k) 查看block size的大小, 例如: `sudo tune2fs -l /dev/sda11 | grep Block` c byte w word, word=2 bytes k 1024 bytes M 1024 KB G 1024 MB ## ACTION -delete 删除匹配到的文件 例: 删除当前目录下所有的.swp文件 `find ./ -type f -name "*.swp" -delete` 执行命令 -exec command ; find解析所有的参数,直到遇到 `;` 结束, 分号需要转义. 例: 查找user为root的文件,并使用chown改变owner为slynux `find ./ -type f -user root -exec chown slynux {} \;` 其中`{}` 是一个特殊的字符串,与-exec一起使用。对于每一个匹配的文件, `{}`被替换为相应的文件名 例: 将修改时间超过10天的jpg文件移动到OLD_DIR目录中 `find . -type f -mtime +10 -name "*.jpg" -exec cp {} OLD_DIR/ \;` ## prune -prune就像一个判断语句, 当prune前面的表达式匹配的时候,prune就会输出true。 如果-prune后面跟的是-o选项,表示如果prune前面的选项成立,就不会执行-o后面的内容了; 如果prune前面的条件不成立,那么将打印输出。 跳过特定的目录 例如: 搜索时,跳过所有的.git目录 `find . \( -name ".git" -prune \) -o \( -type f -print \)` # xargs 我们可以用管道将一个命令的stdout重定向到另一个命令的stdin, 例如: `cat foo.txt | grep "test"` 但是有些命令只能以命令行参数的形式接收数据,而无法通过stdin接收数据, 在这种情况下,就不能通过管道为后续命令提供参数 xargs对从标准输入接收到的数据重新格式化,再将其作为参数提供给其他命令。默认使用空格或者换行符作为分隔参数来处理标准输入. 如果没有指定command, 那么默认的command是echo, xargs可作为find命令中-exec的一种替代方式. OPTIONS: -0, --null 用字符串结束符替代空格分隔输入参数. 引号和反斜杠都看做字面意思,不表示特殊含义. 当输入参数中含有空格,引号,换行符的时候这个参数比较有用,与find的-print0的效果差不多. -d delim, --delimiter=delim 指定输入参数的分隔符,分隔符只能是单个字符,转义字符 例: ``` $ echo "splitXsqlitXsqlitXsqlit" | xargs -d X split sqlit sqlit sqlit ``` 同时结合-n,可以将输入划分为多行 例: ``` $ echo "splitXsqlitXsqlitXsqlit" | xargs -d X -n 2 split sqlit sqlit sqlit ``` 构造参数列表: 假如command的命令格式为: command -p arg1 -l 由于输入参数中arg1是唯一可变的文本, xargs有一个-I选项,可以构造上述形式的参数列表 -I 可以指定一个替换字符串,这个字符串在xargs扩展时会被替换掉。 例: ``` echo "*.txt" | xargs -I {} find ./ -maxdepth 1 -type f -name "{}" -I{} 指定了替换字符串, xargs会将从stdin中接收到的参数替换掉命令序列中的{} 使用-I的时候,命令似乎是在一个循环中执行一样, 如果有n个参数,那么命令就会连同{}一起被执行n次, 而{}在每次执行中都会被替换为对应的参数。 args.txt中的内容如下: $ cat args.txt *.txt *.pdf $ cat args.txt | xargs -I {} find ./ -maxdepth 1 -type f -name "{}" ./example.txt ./args.txt ./english_17.2.pdf find命令会循环2次, {}会分别被替换为 *.txt 和 *.pdf 如果给xargs加上-t选项,就可以看到xargs每次执行命令的详细过程: $ cat args.txt | xargs -t -I {} find ./ -maxdepth 1 -type f -name "{}" find ./ -maxdepth 1 -type f -name *.txt ./example.txt ./args.txt find ./ -maxdepth 1 -type f -name *.pdf ./english_17.2.pdf ``` ## 结合find使用xargs xargs与find结合很方便,但人们通常以一种错误的方式使用他们。例如: `find . -type f -name "*.txt" -print | xargs rm -f` 这样做很危险,有时可能会删除不必要的文件。 我们不能确定find命令输出结果的delimiter是'\n'还是' '。 很多文件名中都可能会包含空格符。而xargs很可能会误认为他们是delimiter,例如hell text.txt常被xargs误认为是hell和text.txt 只要我们把find的输出作为xargs的输入,就必须将-print0与find结合使用,以null来分隔输出。例如: `find . -type f -name "*.txt" -print0 | xargs -0 rm -f` find的选项-print表示输出结果是以'\n'分隔的, -print0的输出是以'\0'分隔的 xargs -0  将\0作为输入定界符. 例: 统计源码目录中所有C程序文件的行数 find source_code_dir_path -type f -name "*.c" -print0 | xargs -0 wc -l