根据man的说法,prune的作用就是不进入目录。
- find
find的简单用法是:
find [path] [expression]
expression主要由options,tests,actions三者组成,这三者又以operators分隔。
expression就是一个表达式,根据operators的优先级,按逻辑运算的方式从左到右执行。options和actions也有返回值,也是表达式中参与逻辑运算的一部分(之前的定式思维没有理解这一点)。
expression的默认operators是"与",即"-a",下面两条命令的含义是一样的:
find ./ -name "2020*" -type d
find ./ -name "2020*" -a -type d
operators的优先级从高到低依次为:
( expr )
! expr
expr1 -a expr2
expr1 -o expr2
expr1, expr2
- prune
-prune是一个actions,它的作用就是不进入目录(即target是目录时,不进入该目录,因此常常与-path一起出现),它本身返回true。
假设有如下目录:
./
./dir0
./dir0/a0.dat
./dir0/a1.txt
./dir0/dir0_0
./dir0/dir0_0/a2.dat
./dir0/dir0_0/a3.txt
./dir0/dir0_1
./dir0/dir0_1/a4.dat
./dir0/dir0_1/a5.txt
./dir1
./dir1/a6.dat
./dir1/a7.txt
./dir1/dir1_0
./dir1/dir1_0/a8.dat
./dir1/dir1_0/a9.txt
./f0.dat
./f1.txt
如果想查找所有的txt文件,但dir0目录下的除外,则可以用如下的命令:
find ./ -path "./dir0" -prune -o -name "*.txt" -print
./dir1/a7.txt
./dir1/dir1_0/a9.txt
./f1.txt
上述命令可以拆解为:
find ./ (expr1 and expr2) or (expr3 and expr4)
其中,
expr1: -path "./dir0"
expr2: -prune
expr3: -name "*.txt"
expr4: -print
执行时,如果遇到./dir0,则expr1返回true,继续执行expr2。expr2是-prune,它的返回值固定是ture,因此(expr1 and expr2)就返回true,or后面的语句不再执行(就不用判断dir0是不是txt,也不会将其打印出来)。整个expression的返回值是true。
注意,man find里有一句话:
If the expression contains no actions other than -prune, -print is performed on all files for which the expression is true.
意思是,如果整个expression为true,而且expression里面除了-prune这个action,就没有其他actions了,则将采用-print这个action,即执行打印动作。
遇到dir0时,虽然整个expression的返回值是true,但这个expression里除了-prune这个action,还有-print这个action(虽然没有执行到,但它确实存在),所以不满足描述的条件,因此也不会把dir0打印出来。
由于-prune的side effect是不进入目录,因此将不进入dir0,其下的所有文件都被跳过。
剩余的情况,expr1将返回false,因此(expr1 and expr2)也返回false,将执行(expr3 and expr4),即遇到txt文件将其打印出来。
- 更多用例
如果把上述用例中的-prune去掉,结果将是:
find ./ -path "./dir0" -o -name "*.txt" -print
./dir0/a1.txt
./dir0/dir0_0/a3.txt
./dir0/dir0_1/a5.txt
./dir1/a7.txt
./dir1/dir1_0/a9.txt
./f1.txt
即把所有的txt文件都找出来了,与find ./ -name "*.txt"没有差别。
执行时,遇到dir0,-path "./dir0"返回true,整个expression返回true(但不把dir0打印出来,见上面分析)。
会进入dir0目录查找其下的文件。
-path是严格匹配,因此dir0下的文件并不能让-path "./dir0"返回true,表达式的后半段-name "*.txt" -print也将被执行,即遇到txt文件打印出来。
剩余的情况,当然也是遇到txt文件打印出来。
如果把末尾的-print去掉,结果将是:
find ./ -path "./dir0" -prune -o -name "*.txt"
./dir0
./dir1/a7.txt
./dir1/dir1_0/a9.txt
./f1.txt
与原始的用例相比,把dir0打印出来了。
执行时,遇到dir0,-path "./dir0"返回true,整个expression返回true。由于整个expression除了-prune没有其他action了,将用-print作为默认的action,因此会把dir0打印出来。
不进入dir0,其下的所有文件都被跳过。
剩余的情况,遇到txt时,整个expression返回true,用-print作为默认的action,执行打印动作。
如果再随意改一下:
find ./ -path "./dir0" -o -name "*.txt" -prune -print
./dir0/a1.txt
./dir0/dir0_0/a3.txt
./dir0/dir0_1/a5.txt
./dir1/a7.txt
./dir1/dir1_0/a9.txt
./f1.txt
这个也是把所有的txt文件都找出来了,与find ./ -name "*.txt"没有差别。
执行时,遇到dir0,-path "./dir0"返回true,整个expression返回true(但不把dir0打印出来,见上面分析)。
会进入dir0目录查找其下的文件。表达式后面部分的-prune并没有在这里执行到,所以不能阻止进入dir0目录。
如果其他地方有一个目录名为aaa.txt,则表达式后面部分,会把aaa.txt打印出来,但不会进入aaa.txt查找其子目录和文件。
附带说明一下,find里的-name, -path等语句带的pattern,如果包含通配符(比如"*"),一般要把这个pattern用双引号包起来,否则通配符可能会被shell先解析到,导致结果与预期不符。