find工具的了解与使用3: -prune

    技术2025-04-03  16

    find的-prune简介

    prune这个词是"修剪"之意. 在find中, 也是这个意思.

    find通常用-prune来忽略一个目录及其之下的文件 (To ignore a directory and the files under it, use -prune).

    manpage中关于-prune的说法是 -prune True; if the file is a directory, do not descend into it. If -depth is given, false; no effect. Because -delete implies -depth, you cannot usefully use -prune and -delete together.

    -prune的简单举例及解释

    For example, to skip the directory `src/emacs’ and all files and directories under it, and print the names of the other files found, do something like this:

    find . -path ./src/emacs -prune -o -print

    这条命令的意思是: 在当前目录下查找, 路径能覆盖 ./src/emacs 的 则执行-prune, 否则, 就print出来.

    按照 find工具的了解与使用1 中提到的 find 命令synopsis (或者叫find命令的句型)

    find [starting-point…] [expression] 其中expression 是由 Tests, Actions, (及Operators)组成

    上述find . -path ./src/emacs -prune -o -print 这么分解: starting-point是 . (当前目录) 后面 -path ./src/emacs -prune -o -print都是Expression. 其中 -path ./src/emacs 是 Tests -prune是Actions -o是Operator, 表示"或" -print 也是Actions

    -path ./src/emacs 是一个Tests, 所以可以判出True或者False. 如果一个文件, 符合./src/emacs这个路径模式, 则为True, 否则为False. 如果是True, 则执行-prune(修剪), 同时后面的-print不执行了, 因为 -o前面是True, 则-o后面不执行. 如果是False, 则不执行-prune, 进而执行-print, 这还是因为-o操作符导致的. 只有-o前面的表达式为False时, -o后面的才会执行. 因此, find . -path ./src/emacs -prune -o -print 其实写完整就是 find . -path ./src/emacs -a -prune -o -print

    用C语言来解释就是

    if ( the_found_file 符合 ./src/emacs 这个path模式) { 执行 -prune ; } else { 执行 -print ; }

    或者, 更简单点

    (所找的文件 符合 ./src/emacs 这个path模式 ) && 执行 prune || 执行 print ;

    -prune的使用举例

    以linux内核代码为例, 通常看一套代码, 使用一些代码编辑器软件来进行的, 比如cscope+vim, 比如sorce insight, 比如 emacs, 等等. 这些代码编辑器软件一般都是建立一个工程, 然后把需要的文件加入到工程中, 一套linux内核代码, 其中不仅仅是C文件和H文件, 也有一些文档, 脚本, 说明之类的非代码文件, 因此创建代码工程时, 这些"无关的"文件时不需要加入的. 那么现在假设有这么一个问题: 将需要假如工程的文件一个一个的列举出来, 保存到文件filelist.txt中

    linux内核代码如下

    [mg@fedora linux-4.9.29]$ pwd /home/mg/code/opensrc/kernel/linux-4.9.29 [mg@fedora linux-4.9.29]$ [mg@fedora linux-4.9.29]$ ls arch Documentation ipc Makefile scripts block drivers Kbuild mm security certs firmware Kconfig net sound COPYING fs kernel README tools CREDITS include lib REPORTING-BUGS usr crypto init MAINTAINERS samples virt [mg@fedora linux-4.9.29]$

    现在认为, 代码里面的Documentation, scripts, samples, tools这些目录下的内容不需要

    并且, arch目录中, 只要x86的, 其他架构的不需要

    [mg@fedora linux-4.9.29]$ cd arch/ [mg@fedora arch]$ ls alpha avr32 frv Kconfig microblaze openrisc score um arc blackfin h8300 m32r mips parisc sh unicore32 arm c6x hexagon m68k mn10300 powerpc sparc x86 arm64 cris ia64 metag nios2 s390 tile xtensa [mg@fedora arch]$

    那么, 怎么用find来完成这个需求呢? 它应该是这样的, find xxxxxxx > filelist.txt

    那么find命令究竟怎么写呢?

    从第一个图中, 知道, 当前目录是 /home/mg/code/opensrc/kernel/linux-4.9.29 因此, find的 starting-point就是这个目录

    为了方便简短, 搞了个变量BASE_DIR

    BASE_DIR="/home/mg/code/opensrc/kernel/linux-4.9.29" find ${BASE_DIR} \ -path "${BASE_DIR}/arch/*" ! -path "${BASE_DIR}/arch/x86*" -prune -o \ -path "${BASE_DIR}/Documentation" -prune -o \ -path "${BASE_DIR}/samples" -prune -o \ -path "${BASE_DIR}/scripts" -prune -o \ -path "${BASE_DIR/tools" -prune -o \ -type f \ -print > filelist.txt

    上述脚本的解释:

    -path "${BASE_DIR}/arch/*" ! -path "${BASE_DIR}/arch/x86*" -prune -o \

    这一行的意思是, 将arch目录下, 那些不是x86的全prune掉. 而arch目录下是x86的, 则 进入后面的express接着执行 (因为是-o). 注意, 这里的 -path "${BASE_DIR}/arch/*" 不能写成 -path "${BASE_DIR}/arch*" 或 -path "${BASE_DIR}/arch" <这里涉及到-path怎么匹配模式的, 具体见find工具的了解与使用2: -path> 因为写成后面两种, 会匹配 ${BASE_DIR}/arch 这个目录本身. 而 ${BASE_DIR}/arch 与 后面的 ! -path "${BASE_DIR}/arch/x86*" 运算后(默认是"与"运算(-a运算符)), 是True, 所以, 相当于 ${BASE_DIR}/arch 这个目录被prune掉, 那么, arch下的所有子目录, 不论是不是x86, 都统统被prune了. find就不会再进入arch内部查找文件了.

    同理, ! -path "${BASE_DIR}/arch/x86*" 这部分, 也不能写成 ! -path "${BASE_DIR}/arch/x86" 因为 -path "${BASE_DIR}/arch/x86" 这个不会匹配 x86目录里的文件, 只会匹配x86目录本身.

    总之, 要充分理解 -path的作用方式, 这个见 find工具的了解与使用2: -path>

    上述那段脚本, 每个带 " -prune -o " 的行, 都是prune掉不代码工程不需要的目录或文件, 最后的 -type f是只要 普通文件, 最后的-print是, 将最终符合要求的文件print出来, 重定向到filelist.txt中.

    上述脚本, 若完整的, 不带任何省略的写法, 则是

    BASE_DIR="/home/mg/code/opensrc/kernel/linux-4.9.29" find ${BASE_DIR} \ -path "${BASE_DIR}/arch/*" -a ! -path "${BASE_DIR}/arch/x86*" -a -prune -o \ -path "${BASE_DIR}/Documentation" -a -prune -o \ -path "${BASE_DIR}/samples" -a -prune -o \ -path "${BASE_DIR}/scripts" -a -prune -o \ -path "${BASE_DIR/tools" -a -prune -o \ -type f -a \ -print > filelist.txt

    注意: 上述find命令中, 最后一行的-print加与不加, 结果不一样. 如果有-print, 则那些被prune的目录本身(例如 ${BASE_DIR}/samples)就不会出现在filelist.txt中, 如果没有最后的-print, 则那些被prune的目录本身会出现在filelist.txt中.

    这是因为两个Tests之间若没有Operator, 则认为它们之间是-a. 所以, 如果最后的-print没有, 则由于都是-o(或), 所以被prune的会打出来, 不被prune的也会打出来. 但如果有最后的-print, 由于-print默认与-type f之间是-a, 所以相当于不被prune的才打出来, 而被prune的就不打了.

    manpage中有这段描述:

    Please note that -a when specified implicitly (for example by two tests appearing without an explicit operator between them) or explicitly has higher precedence than -o. This means that find . -name afile -o -name bfile -print will never print afile.

    例子:

    #现在有这些文件和目录 [mg@ukylin: virt]$ ls kvm lib Makefile [mg@ukylin: virt]$ [mg@ukylin: virt]$ # 先看下所有文件/目录都有啥 [mg@ukylin: virt]$ find . . ./Makefile ./lib ./lib/Makefile ./lib/irqbypass.c ./lib/Kconfig ./kvm ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c [mg@ukylin: virt]$ [mg@ukylin: virt]$ pwd /home/mg/workplace/code/github/linux/virt # 下面的命令中 # 相当于 find . Test1 Action1 -o Test2 # 裁减掉lib, 最后不加-print, 则被裁减的lib也会打出来, 这是因为 # -o的前面 -path "./lib" -prune会打出./lib # -o的后面 -type f 会打出没被裁剪掉的文件和目录 # If the whole expression contains no actions other than # -prune or -print, -print is performed on all files for # which the whole expression is true. [mg@ukylin: virt]$ find . -path "./lib" -prune -o -type f ./Makefile ./lib ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c # 下面的命令中 # 单独裁剪lib, 则也会打出./lib [mg@ukylin: virt]$ find . -path "./lib" -prune ./lib [mg@ukylin: virt]$ find . -path "./lib" -prune -print ./lib # 下面的命令中, 不会打出被prune的 "./lib" # 相当于 find . Test1 Action1 -o Test2 Action2 # 相当于 find . Test1 -a Action1 -o Test2 -a Action2 # 相当于 find . (Test1 -a Action1) -o (Test2 -a Action2) [mg@ukylin: virt]$ find . -path "./lib" -prune -o -type f -print ./Makefile ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c # 下面的命令中 # -print和-type f之间其实是相当于有个-a的, 这个-a是对 # (-type f) 和 (-print) 进行 "AND" 操作. # 这个命令相当于 find . Test1 Action1 -o Test2 -a Action2 # Test1 = (-path "./lib"), Action1 = (-prune) # Test2 = (-type f), Action2 = (-print) # 由于-a的优先级高于-o, 所以-a结合的是Test2和Action2, # 即, 这个命令是这么运作的: # find . Test1 Action1 -o (Test2 -a Action2) # 相当于find . (Test1 -a Action1) -o (Test2 -a Action2) [mg@ukylin: virt]$ find . -path "./lib" -prune -o -type f -a -print ./Makefile ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c # 显式执行 find . (Test1 -a Action1) -o (Test2 -a Action2) [mg@ukylin: virt]$ find . -path "./lib" -a -prune -o -type f -a -print ./Makefile ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c # 下面的命令中, 加上括号(用\进行转义), # 强迫 -print与前面整个进行"与"操作 # 相当于 find . (Test1 Action1 -o Test2) -a Action2 # 相当于 find . ((Test1 -a Action1) -o Test2) -a Action2 [mg@ukylin: virt]$ find . \( -path "./lib" -prune -o -type f \) -print ./Makefile ./lib ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c [mg@ukylin: virt]$ # 更加显式的展现执行时的-o, -a操作 [mg@ukylin: virt]$ find . \( -path "./lib" -a -prune \) \ > -o \( -type f -a -print \) ./Makefile ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c # find . (Test1 -a Action1 -a Action2) -o (Test2 -a Action3) [mg@ukylin: virt]$ find . \( -path "./lib" -a -prune -a -print \) \ > -o \( -type f -a -print \) ./Makefile ./lib ./kvm/vfio.h ./kvm/eventfd.c ./kvm/coalesced_mmio.h ./kvm/vfio.c ./kvm/irqchip.c ./kvm/async_pf.c ./kvm/coalesced_mmio.c ./kvm/Kconfig ./kvm/async_pf.h ./kvm/kvm_main.c

    -prune小结

    -prune一般跟-path结合使用, 使用过程中, 要充分理解-path的匹配方式, 还有, 如果-prune将某个目录prune掉了, 那么这个目录里的任何子文件/子目录都在后续不可见了, 不考虑了.

    参考

    find命令的-prune用法 http://cscope.sourceforge.net/large_projects.html

    Processed: 0.012, SQL: 12