本文原创发布于微信公众号“洛奇看世界”
我之前分享过一篇《一份资深程序员的 grep 笔记》,现在分享一篇我的 find
笔记。
find
命令很复杂,一开始总是记不住如何使用,后来每次使用时就把没见过的用法记下来,最后形成了这篇笔记的主体。
find
命令很复杂,虽然我已经用过几百甚至上千遍了,但时间长了还是经常会忘记具体的用法,这时候我就会回到笔记中快速找下该如何使用,因为是完全按照自己的习惯记录的笔记,所以回忆起来也很快,实在想不起了,就照着用法 copy 一下,哈哈。
下面是本文的目录内容,可以根据需要阅读:
文章目录
- 1. `find` 的命令格式
- 2. `find` 的常用选项
- 0. `-print` 和 `-print0` 选项
- 1. `-name/-iname` 选项, 指定查找的文件名
- 2. `-path/-ipath` 选项,按指定的 path 模式搜索
- 3. `-type` 选项, 指定查找的文件类型
- 4. `-maxdepth` 和 `-mindepth` 选项, 指定查找的深度
- 5. `-depth` 选项
- 6. `-size` 选项, 根据文件的大小搜索
- 7. `-empty` 选项,搜索空文件/空目录
- 8. `-atime/-mtime/-ctime` 选项,根据时间搜索
- 9. `-perm` 选项,根据权限进行搜索
- 10. `-prune` 选项 (排除需要配合 "-o" 选项)
- 11. `-exec` 选项
- 3. `find` 综合示例
- 4. `find` 最常见的用法
- 5. `find` 命令的资料
- 6. 近期文章
- 7. 其它
1. find
的命令格式
find [-H] [-L] [-P] [-D debugopts] [-Olevel] [path...] [expression]
看起来很复杂,除去调试的各种选项,真正执行的find命令格式为:
find [path...] [expression]
- 默认 path 为当前目录
- 默认 expression 为
-print
,打印当前搜索结果并以 ‘\n’ 进行分割 - expression 由 4 类动作的一个或多个组成: operators, options, tests, 和actions
2. find
的常用选项
0. -print
和 -print0
选项
find默认的动作是 -print
, 使用 0x0a
(\n
字符)对查找的结果进行分隔。
rocky@guyongqiangx:/mnt/c$ find . -maxdepth 1 -type f -name "*.sys"
./hiberfil.sys
./pagefile.sys
./swapfile.sys
rocky@guyongqiangx:/mnt/c$
rocky@guyongqiangx:/mnt/c$ find . -maxdepth 1 -type f -name "*.sys" | hexdump -Cv
00000000 2e 2f 68 69 62 65 72 66 69 6c 2e 73 79 73 0a 2e |./hiberfil.sys..|
00000010 2f 70 61 67 65 66 69 6c 65 2e 73 79 73 0a 2e 2f |/pagefile.sys../|
00000020 73 77 61 70 66 69 6c 65 2e 73 79 73 0a |swapfile.sys.|
0000002d
如果使用-print0
,则使用 0x00
(null
) 字符对结果进行分隔。
rocky@guyongqiangx:/mnt/c$ find . -maxdepth 1 -type f -name "*.sys" -print0
./hiberfil.sys./pagefile.sys./swapfile.sys
rocky@guyongqiangx:/mnt/c$ find . -maxdepth 1 -type f -name "*.sys" -print0 | hexdump -Cv
00000000 2e 2f 68 69 62 65 72 66 69 6c 2e 73 79 73 00 2e |./hiberfil.sys..|
00000010 2f 70 61 67 65 66 69 6c 65 2e 73 79 73 00 2e 2f |/pagefile.sys../|
00000020 73 77 61 70 66 69 6c 65 2e 73 79 73 00 |swapfile.sys.|
0000002d
从上面可以看到,使用 -print0
后所有结果中间都是 0x00
,没有换行 \n
, 导致紧接着的命令行提示符和输出结果连在了一起。
注意,
-print0
针对的是查找结果之前的\n
转\0
。
如果查找的文件名本身就含有空格,则find
后-print0
仍然会显示空格文件。所以-print0
实现的是\n
转\0
的标记,可以使用其他工具将\0
标记替换掉,如 xargs,tr 等。
1. -name/-iname
选项, 指定查找的文件名
查找一个文件需要包含路径和文件名两个部分
-name
和 -iname
对文件名(不包含路径)进行匹配,匹配一个文件的 basename 部分。
例如:
rocky@guyongqiangx:/mnt/c$ find . -name "*.sys"
./hiberfil.sys
./pagefile.sys
./swapfile.sys
由于文件名不能包含 /
字符,所以不能在 -name
的模式中使用/
字符:
rocky@guyongqiangx:/public/op-tee$ find . -name "x/y.xml"
find: warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '-name `x/y.xml'' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ `x/y.xml''.
相比之下,使用 -path
选项可以同时对路径和文件名进行匹配,包含 dirname+basename 两部分。
所以想要在指定目录下搜索某目录中的某文件,应该使用 -path
而不是 -name
。
rocky@guyongqiangx:/public/op-tee$ find . -path "x/y.xml"
./arm-v8/qemu/gdb-xml/i386-32bit.xml
./arm-v8/qemu/gdb-xml/s390-vx.xml
./arm-v8/.repo/manifest.xml
...
./arm-v8/.repo/manifests/rpi3.xml
./arm-v8/.repo/manifests/default.xml
./arm-v8/manifest-20190101-r.xml
./arm-v8/manifest-20190101.xml
如果指向搜索 .repo
目录下的 xml 文件,如 .repo/*.xml
:
# 没有用引号包含*字符,估计被展开了
rocky@guyongqiangx:/public/optee/rpi3$ find . -path ./.repo/*.xml
./.repo/manifest.xml
rocky@guyongqiangx:/public/optee/rpi3$ find . -path './.repo/*.xml'
./.repo/manifest.xml
./.repo/manifests/hikey960.xml
./.repo/manifests/dra7xx.xml
...
rocky@guyongqiangx:/public/optee/rpi3$ find . -path './.repo*.xml'
./.repo/manifest.xml
./.repo/manifests/hikey960.xml
./.repo/manifests/dra7xx.xml
...
2. -path/-ipath
选项,按指定的 path 模式搜索
-path
选项的理解比较难,关于 -path
选项,man page
是这样说的:
-path pattern
File name matches shell pattern pattern. The metacharacters do not treat `/' or `.' specially; so, for exam-
ple,
find . -path "./sr*sc"
will print an entry for a directory called `./src/misc' (if one exists). To ignore a whole directory tree,
use -prune rather than checking every file in the tree. 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
Note that the pattern match test applies to the whole file name, starting from one of the start points named
on the command line. It would only make sense to use an absolute path name here if the relevant start point
is also an absolute path. This means that this command will never match anything:
find bar -path /foo/bar/myfile -print
The predicate -path is also supported by HP-UX find and will be in a forthcoming version of the POSIX stan-
dard.
特别提到:the pattern match test applies to the whole file name
。
因此,搜索是是按照完整文件名(路径+文件名)进行匹配的,而不是只匹配路径的一部分。
- 用法示例 1
在目录 rocky 下查找名为 smon.bin 的文件,其路径满足格式为 “rocky/7271b0”,然后计算器 md5 值。
这里使用 md5sum 查看搜索结果的哈希值,主要是为了确认结果中有哪些文件是一样的。
很显然,第一个 find 命令的结果中,前两个文件是一样的。
$ find rocky/ -path "rocky/*7271b0*" -type f -name smon.bin 2>/dev/null | xargs md5sum
84c93b44e0409df97fdbf00e57c2d91e rocky/bootloader/src/objs/7271b0/smon.bin
84c93b44e0409df97fdbf00e57c2d91e rocky/bootloader/src/security/7271b0/smon.bin
e7693ca7f7fbea6b66bcf276e00eb46f rocky/bootloader/src/security/7271b0/zb/smon.bin
$ find rocky/ -path "7271*security/7271b0/smon.bin" -type f -name smon.bin 2>/dev/null
rocky/bootloader/src/security/7271b0/smon.bin
$ find rocky/ -path "*security/7271b0/smon.bin" -type f -name smon.bin 2>/dev/null
rocky/bootloader/src/security/7271b0/smon.bin
- 用法示例2
这个搜索有点复杂,大意是:
在当前目录中搜索 size 大于 5M 的文件,但要排除满足 “*stbgcc-6.3-1.8*
” 和 “*linuxkernel.aarch64-linux.97271.dbg*
” 模式的路径
$ find . -path "*stbgcc-6.3-1.8*" -prune \
-o -path "*linuxkernel.aarch64-linux.97271.dbg*" -prune \
-o -type f -size +5M \
-a \( ! -iname *.xz -a ! -iname *.o -a ! -iname *.c -a ! -iname *.h -a ! -iname *.ko \) \
-print 2>/dev/null
-path
和-prune
配合使用,排除满足 “*stbgcc-6.3-1.8*
” 和 “*linuxkernel.aarch64-linux.97271.dbg*
” 模式的路径;- 多个 “
! -iname
” 选项联合使用,排除 "*.{xz,o,c,h,ko}
"文件
- 用法示例3
在当前目录下搜索名字中包含 “keystore” 的目录,但要排除 out
和 .repo
目录,而且搜索到目录名中包含 “keystore” 字符串时不再继续搜索子目录。
这是我在 Andrid Q 上学习 keystore 时,使用的一条命令,目的是为了找到所有 keystore 相关的目录。
src-km$ find . -path ./out -prune -o -path ./.repo -prune \
-o -type d -iname "*keystore*" -print -prune
./developers/build/prebuilts/gradle/BasicAndroidKeyStore
./cts/tests/security/src/android/keystore
./cts/tests/tests/keystore
./tools/tradefederation/core/global_configuration/com/android/tradefed/util/keystore
./tools/tradefederation/core/tests/src/com/android/tradefed/util/keystore
./external/bouncycastle/repackaged/bcprov/src/main/java/com/android/org/bouncycastle/jcajce/provider/keystore
./external/bouncycastle/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore
./development/samples/browseable/BasicAndroidKeyStore
./system/security/keystore
./system/security/keystore-engine
./system/hardware/interfaces/wifi/keystore
./frameworks/base/services/core/java/com/android/server/locksettings/recoverablekeystore
./frameworks/base/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore
./frameworks/base/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest
./frameworks/base/keystore
./frameworks/base/core/java/android/security/keystore
./frameworks/base/core/tests/coretests/src/android/security/keystore
3. -type
选项, 指定查找的文件类型
一般需要搜索的文件类型有普通文件(f),目录(d),链接文件(l)。
其实最常用的就是搜索文件(-type f
)或目录(-type d
)。
# 搜索当前目录下的所有"default.xml"文件
rocky@guyongqiangx:/public/op-tee$ find . -type f -name "default.xml"
./aosp-latest/.repo/manifests/default.xml
./aosp/.repo/manifests/default.xml
./rpi3/.repo/manifests/default.xml
./tsinghua-aosp/.repo/manifests/default.xml
./arm-v8/.repo/manifests/default.xml
# 搜索当前目录下的所有".repo"目录
rocky@guyongqiangx:/public/op-tee$ find . -type d -name ".repo"
./aosp-latest/.repo
./aosp/.repo
./rpi3/.repo
./tsinghua-aosp/.repo
./arm-v8/.repo
# 搜索/usr目录下名称中包含 mpfr,gmp 和 mpc 的目录
ygu@guyongqiangx:~$ find /usr -type d -a \( -iname "*mpfr*" -o -iname "*gmp*" -o -iname "*mpc*" \) -print 2>/dev/null
/usr/src/linux-headers-4.4.0-135-generic/include/config/bridge/igmp
/usr/src/linux-headers-4.4.0-141-generic/include/config/bridge/igmp
/usr/lib/firefox/gmp-clearkey
/usr/local/lib/python3.6/site-packages/gmpy2-2.0.8-py3.6.egg-info
/usr/local/lib/python3.4/dist-packages/gmpy2-2.0.8.dist-info
/usr/share/doc/libgmpxx4ldbl
/usr/share/doc/libmpc3
/usr/share/doc/libmpfr4
/usr/share/doc/libmpfr-dev
/usr/share/doc/libgmp-dev
/usr/share/doc/libgmp10
/usr/share/doc/libmpc-dev
4. -maxdepth
和 -mindepth
选项, 指定查找的深度
使用 -maxdepth=1
指定查找当前目录下的文件,并计算文件的 md5 值
$ tree .
.
├── bin
│ └── makegpt
├── boot1.bin
├── gpt.bin
├── gpt.bin.gen.c
├── gpt.cfg
├── lib64
│ └── libc++.so
├── makegpt
├── makegpt-example.tar.bz2
├── md5sums
└── show devices.png
2 directories, 10 files
$ find . -maxdepth 1 -type f 2>/dev/null -print0 | xargs -0 md5sum
d41d8cd98f00b204e9800998ecf8427e ./md5sums
5fbd206429487cf5e646cc17c7899c06 ./gpt.bin
daaa899a137bbd8fca5a248f28631cd5 ./makegpt
55d53bc118b7aa5ff77b5dda3f4da9f8 ./boot1.bin
c4a6fb95cef1d5c48a1f2e405ab66a03 ./gpt.cfg
f78feed2a3d15cf7bb62a1ab52926183 ./show devices.png
236c6bd2bc3aaa2db262e0c6ed145ed3 ./gpt.bin.gen.c
f21b13368d4311114b64a7d090d5c42f ./makegpt-example.tar.bz2
5. -depth
选项
-depth
指定深度优先
使用 -depth
选项搜索到目录时,先处理目录中文件(子目录),再处理目录本身。
对于 -delete
这个 action,它隐含 -depth
选项。
# 没有指定深度优先
$ find rpi3 -name "*repo*" 2>/dev/null
rpi3/repo-init-cmd.txt
rpi3/.repo
rpi3/.repo/manifests.git/.repo_config.json
rpi3/.repo/repo
rpi3/.repo/repo/repoc
rpi3/.repo/repo/.git/.repo_config.json
rpi3/.repo/repo/repo
rpi3/repo-init.log
# 指定深度优先
$ find rpi3 -depth -name "*repo*" 2>/dev/null
rpi3/repo-init-cmd.txt
rpi3/.repo/manifests.git/.repo_config.json
rpi3/.repo/repo/repoc
rpi3/.repo/repo/.git/.repo_config.json
rpi3/.repo/repo/repo
rpi3/.repo/repo
rpi3/.repo
rpi3/repo-init.log
6. -size
选项, 根据文件的大小搜索
- 查找当前目录下大于 1024M 的文件
$ find . -type f -size +1024M 2>/dev/null
./src-arm-v8.repo.tar.bz2
./arm-v8/.repo/project-objects/linaro-swg/linux.git.git/objects/pack/pack-ad4...8f.pack
7. -empty
选项,搜索空文件/空目录
- 查找空目录
rocky@guyongqiangx:/public/op-tee$ find . -maxdepth 3 -type d -empty | xargs ls -lh
./git-repo/.git/branches:
total 0
./rpi3/.idea/inspectionProfiles:
total 0
- 查找空文件
rocky@guyongqiangx:/public/op-tee$ find . -maxdepth 3 -type f -empty | xargs ls -lh
-rw-rw-r-- 1 ygu ygu 0 Nov 26 2018 ./arm-tf/optee-bin/bl32_extra2.bin
-rw-rw-r-- 1 ygu ygu 0 Nov 18 2018 ./arm-v8/qemu/qapi-gen-timestamp
8. -atime/-mtime/-ctime
选项,根据时间搜索
- 搜索 /tmp 下 3 天内修改过内容的 sh 文件,因为是文件内容,所以不考虑搜索目录
$ find /tmp -type f -mtime -3 -name "*.sh"
9. -perm
选项,根据权限进行搜索
搜索 /tmp 下所有者具有可读可写可执行权限的 sh 文件。
$ find /tmp -type f -perm -0700 -name '*.sh'
10. -prune
选项 (排除需要配合 “-o” 选项)
10.1 示例1
搜索当前目录下名字带 ‘keymaster’ 的目录,但忽略 ‘out’ 和 ‘.repo’ 目录
这条命令在前面说过了。
# find . -path ./out -prune -o -path ./.repo -prune -o -type d -iname "*keymaster*" -print
$ find . -name out -prune -o -name .repo -prune -o -type d -iname "*keymaster*" -print
./vendor/broadcom/refsw/BSEAV/lib/security/sage/keymaster
./vendor/broadcom/refsw/BSEAV/lib/security/astra/keymaster
./vendor/broadcom/bcm_platform/hals/keymaster
./hardware/interfaces/keymaster
./hardware/interfaces/keymaster/4.0/support/include/keymasterV4_0
./hardware/libhardware/tests/keymaster
./hardware/qcom/keymaster
./test/vts-testcase/hal/keymaster
./test/vts-testcase/fuzz/config/keymaster
./test/vts-testcase/hal-trace/keymaster
./device/generic/goldfish/keymaster
./external/autotest/server/site_tests/brillo_Keymaster
./system/keymaster
./system/keymaster/include/keymaster
./system/keymaster/android_keymaster
./system/core/trusty/keymaster
./system/core/trusty/keymaster/include/trusty_keymaster
./system/security/keystore/binder/android/security/keymaster
./frameworks/base/core/java/android/security/keymaster
但是,上面显示的目录中,如果已经搜索到 keymaster 目录,还会继续往下搜索,例如下面的结果:
./system/core/trusty/keymaster
./system/core/trusty/keymaster/include/trusty_keymaster
因此,在已经搜索到 keymaster 的目录后,应该停止当前目录搜索,并转到下一个目录中:
$ find . -name out -prune -o -name .repo -prune -o -type d -iname "*keymaster*" -print -prune
./vendor/broadcom/refsw/BSEAV/lib/security/sage/keymaster
./vendor/broadcom/refsw/BSEAV/lib/security/astra/keymaster
./vendor/broadcom/bcm_platform/hals/keymaster
./hardware/interfaces/keymaster
./hardware/libhardware/tests/keymaster
./hardware/qcom/keymaster
./test/vts-testcase/hal/keymaster
./test/vts-testcase/fuzz/config/keymaster
./test/vts-testcase/hal-trace/keymaster
./device/generic/goldfish/keymaster
./external/autotest/server/site_tests/brillo_Keymaster
./system/keymaster
./system/core/trusty/keymaster
./system/security/keystore/binder/android/security/keymaster
./frameworks/base/core/java/android/security/keymaster
如果上面的 find 命令不加 -print
,则会打印 out
和 .repo
目录:
$ find . -name out -prune -o -name .repo -prune -o -type d -iname "*keymaster*"
./out
./vendor/broadcom/refsw/BSEAV/lib/security/sage/keymaster
...
./external/tinyxml2/resources/out
./external/cldr/tools/java/org/unicode/cldr/draft/keyboard/out
./.repo
./system/keymaster
...
./frameworks/base/core/java/android/security/keymaster
以下三个命令的效果一样(可以正确显示):
find . -name out -prune -o -name .repo -prune -o -type d -iname "*keymaster*" -print
find . -name out -prune -o -name .repo -prune -o -type d -iname "*keymaster*" -a -print
find . -path ./out -prune -o -path ./.repo -prune -o -type d -iname "*keymaster*" -print
以下命令会打印所有文件:
find . -name out -prune -o -name .repo -prune -o -type d -iname "*keymaster*" -o -print
这里的 -prune
属于 action,而不是 option,和 -print
等一样,-prune
和 -print
的结果都为 true。
10.2 示例2
在当前目录下查找 “file.c” 文件,但请忽略 “linux-custom” 目录
- 基本做法, 在当前目录下查找 “file.c” 文件
$ find . -type f -iname file.c
./dosfstools-4.1/src/file.c
./host-squashfs-e38956b92f738518c29734399629e7cdb33072d3/kernel/fs/squashfs/file.c
./linux-custom/fs/configfs/file.c
./linux-custom/fs/affs/file.c
./linux-custom/fs/ntfs/file.c
...
./linux-custom/arch/um/os-Linux/file.c
./host-cmake-3.15.5/Utilities/cmcurl/lib/file.c
./host-cmake-3.15.5/Tests/GhsMulti/GhsMultiUnsupportedTargets/file.c
./xfsprogs-5.2.1/spaceman/file.c
./xfsprogs-5.2.1/io/file.c
- 错误做法,使用 “-a” 选项,没有结果
$ find . -path "./linux-custom" -prune -type f -iname file.c
$ find . -path "./linux-custom" -prune -a -type f -iname file.c
- 正确做法,使用 “-o” 选项
$ find . -path "./linux-custom" -prune -o -type f -iname "file.c" -print
./dosfstools-4.1/src/file.c
./host-squashfs-e38956b92f738518c29734399629e7cdb33072d3/kernel/fs/squashfs/file.c
./host-cmake-3.15.5/Utilities/cmcurl/lib/file.c
./host-cmake-3.15.5/Tests/GhsMulti/GhsMultiUnsupportedTargets/file.c
./xfsprogs-5.2.1/spaceman/file.c
./xfsprogs-5.2.1/io/file.c
10.3 示例3
在当前目录下搜索名字带有 “fastboot” 或 “fb” 的文件, 并忽略 “.git”, “gen” 和 “objs” 目录
$ find . -type d \( -name .git -o -iname gen -o -name objs \) -prune -o -type f \( -iname "*fastboot*" -o -iname "*fb*" \) -print
./android/fastboot.h
./android/android_fastboot.c
10.4 示例4
搜索 /usr/lib
和 /usr/local/lib
下名字包含 “python” 的目录
rocky@guyongqiangx:~$ find /usr/lib /usr/local/lib -type d -iname "python*" 2>/dev/null
/usr/lib/python-tz
/usr/lib/python3.4
/usr/lib/python3
/usr/lib/python3/dist-packages/python_dateutil-2.0.egg-info
/usr/lib/python3/dist-packages/python_debian-0.1.21_nmu2ubuntu2.egg-info
/usr/lib/pymodules/python2.7
/usr/lib/pyshared/python2.7
/usr/lib/python3.7
/usr/lib/python2.7
/usr/lib/python2.7/dist-packages/twisted/python
/usr/lib/python2.7/dist-packages/python_dateutil-1.5.egg-info
/usr/lib/python2.7/dist-packages/python_debian-0.1.21_nmu2ubuntu2.egg-info
/usr/local/lib/python3.4
显然,下面这样的结果,在上层文件夹名字中已经包含 python,不再需要往下查找了,所以使用:
-print
选项打印,- 然后使用
-prune
选项退出当前目录搜索即可。
rocky@guyongqiangx:~$ find /usr/lib /usr/local/lib -type d -iname "python*" -print -prune 2>/dev/null
/usr/lib/python-tz
/usr/lib/python3.4
/usr/lib/python3
/usr/lib/pymodules/python2.7
/usr/lib/pyshared/python2.7
/usr/lib/python3.7
/usr/lib/python2.7
/usr/local/lib/python3.4
/usr/local/lib/python3.7
/usr/local/lib/python2.7
11. -exec
选项
# 命令格式: -exec command {} ;
基于查找结果执行命令 command,命令对象用 {} 代替,后面需要接分号。
在 out 目录下查找名为 IMAGES 或 RADIO 的目录,并列举目录下的内容:
$ find out -type d -a \( -name IMAGES -o -name RADIO \) -exec ls -lh {} \;
total 2.9M
-rw-r--r-- 1 rg935739 users 1.5M Feb 3 15:17 bootloader.dev.img
-rw-r--r-- 1 rg935739 users 1.5M Feb 3 15:17 bootloader.prod.img
-rw-r--r-- 1 rg935739 users 380 Feb 3 15:17 dtbo.jasper.img
total 1.2G
-rw-r--r-- 1 rg935739 users 64M Feb 3 15:17 boot.img
-rw-r--r-- 1 rg935739 users 45K Feb 3 15:18 cache.img
-rw-r--r-- 1 rg935739 users 1.0M Feb 3 15:18 dtbo.img
-rw-r--r-- 1 rg935739 users 380 Feb 3 15:18 dtbo.jasper.img
...
3. find
综合示例
- 搜索指定日期范围的文件
搜索 /test
下 2017-06-03 到 2017-06-06 之间修改过的文件。
$ find /test -type f -newermt 2017-06-03 -a ! -newermt 2017-06-06
或者,创建两个临时文件,并用touch修改这两个文件的修改时间,然后 find -newer
去参照这两个文件。
$ touch -m -d 2017-06-03 tmp1.txt
$ touch -m -d 2017-06-06 tmp2.txt
$ find /test -type f -newer tmp1.txt -a ! -newer tmp2.txt
不过这样会把 tmp2.txt 也搜索出来,因为 newer 搜索的是比 xxx 文件更新,取反则表示更旧或时间相同。
- 获取文件绝对路径
当 find 结合管道,而管道后的命令很可能想要获取到搜索到的文件的绝对路径,或者说是全路径。
而问题是,当 find 的搜索路径是相对路径时,搜索出来的显示结果也是以相对路径显示的。
# 方法1: 搜索前先 pwd 或使用 $PWD 环境变量
$ find $(pwd)
$ find $PWD
# 方法2: 执行 readlink,它不仅解析软链接,也可以使用 -f 选项解析普通文件
$ find . -exec readlink -f {} \;
/tmp/test
/tmp/test/a.png
/tmp/test/b.png
/tmp/test/c.png
# 方法3: 使用 bash 的波浪号扩展 `~+`
# $ echo ~+
/public/op-tee
$ find ~+
/tmp/test
/tmp/test/a.png
/tmp/test/b.png
/tmp/test/c.png
rocky@guyongqiangx:/public/optee$ find ~+ -maxdepth 1 -type d
/public/optee
/public/optee/rpi3-3.9.0
/public/optee/rpi3
/public/optee/git-repo
- 从结果中排除目录自身
find 搜索目录时,总是会将搜索路径自身也包含到搜索结果中。你可能会想办法排除它。
排除的方法是,加上一个 -path
选项并取反,-path
的参数和 find 的搜索路径参数必须一致。
# 搜索结果中包含当前目录 "/public/op-tee"
$ find /public/op-tee -type d
/public/op-tee
/public/op-tee/aosp-latest
/public/op-tee/aosp
/public/op-tee/rpi3
# 搜索结果中排除当前目录 "/public/op-tee"
# $ find /public/op-tee -maxdepth 1 -type d ! -path /public/op-tee
$ find /public/op-tee -type d ! -path /public/op-tee
/public/op-tee/aosp-latest
/public/op-tee/aosp
/public/op-tee/rpi3
- 处理带有空格的文件名
# 查找 C 盘下 大于 1G 的文件
#
# find 结果的文件名中有空格,传递给后续操作出错了
# 例如下面出错的:
# $ find . -maxdepth 4 -type f -size +1024M -print 2>/dev/null
# ./LargeFiles/Virtual Machines/Ubuntu/Ubuntu_x64-s001.vmdk
# ./LargeFiles/Virtual Machines/Ubuntu/Ubuntu_x64-s002.vmdk
#
rocky@guyongqiangx:/mnt/c$ find . -maxdepth 6 -type f -size +1024M 2>/dev/null | xargs ls -lh
ls: cannot access './LargeFiles/Virtual': No such file or directory
ls: cannot access 'Machines/Ubuntu/Ubuntu_x64-s001.vmdk': No such file or directory
ls: cannot access './LargeFiles/Virtual': No such file or directory
ls: cannot access 'Machines/Ubuntu/Ubuntu_x64-s002.vmdk': No such file or directory
...
-rwxrwxrwx 1 ygu ygu 3.0G May 24 2018 './$GetCurrent/media/sources/install.esd'
-rwxrwxrwx 1 ygu ygu 5.8G Feb 26 15:00 ./LargeFiles/SecureDisk/securedisk.pgd
...
rocky@guyongqiangx:/mnt/c$
一般情况,类似上面的 find 命令运行的很好,但是如果找到的文件名包含空格,上面的命令运行就可能会出问题了。
有一个参数 -print0
,与默认的 -print
相比,输出的序列不是以空格分隔,而是以 \0
(null 字符)分隔。
而 xargs 也有一个参数 -0
,可以接受以 \0
(null) 而非空格间隔的输入流。
所以文件名带空格的问题就很好解决了:
find . -maxdepth 4 -type f -size +1024M -print0 2>/dev/null | xargs -0 ls -lh --block-size=M
$ find . -maxdepth 4 -type f -size +1024M -print0 2>/dev/null | xargs -0 ls -lh --block-size=M
-rwxrwxrwx 1 ygu ygu 2978M May 24 2018 './$GetCurrent/media/sources/install.esd'
-rwxrwxrwx 1 ygu ygu 5934M Feb 28 01:06 ./LargeFiles/SecureDisk/securedisk.pgd
-rwxrwxrwx 1 ygu ygu 10669M Feb 11 2018 './LargeFiles/Virtual Machines/Ubuntu-14.04/Ubuntu.vmdk'
-rwxrwxrwx 1 ygu ygu 1921M Feb 22 17:39 './LargeFiles/Virtual Machines/Ubuntu/Ubuntu_x64-s001.vmdk'
-rwxrwxrwx 1 ygu ygu 1979M Feb 22 17:39 './LargeFiles/Virtual Machines/Ubuntu/Ubuntu_x64-s002.vmdk'
...
-rwxrwxrwx 1 ygu ygu 1068M Apr 13 2019 ./Python/mysql-5.7.26-winx64/lib/mysqlserver.lib
-rwxrwxrwx 1 ygu ygu 1758M Feb 28 2018 ./SecureCRTLogs/2018-02-27_144524-stbszx-bld-6.log
4. find
最常见的用法
前面第 2 节大概列举了 find 名的 12 种用法,非常复杂。
其实我平时最常用的只有以下两条:
# 在当前目录下查找名字中包含 "keymaster" 的文件,忽略文件名大小写
find . -type f -iname "*keymaster*"
# 在当前目录下查找名字中包含 "keymaster" 的目录,忽略目录名字的大小写
find . -type d -iname "*keymaster*"
有时候在 find 的输出结果中会包含一些找不到目录,没有权限之类的错误信息,此时可以使用 2>/dev/null
来将错误信息输出到 /dev/null
设备中。
例如:
find . -type f -iname "*keymaster*" 2>/dev/null
我经常使用下面这个 find 命令来查找代码的文档,这一点我在《# 读代码,没有文档怎办?》中说过。
find . -type f \( -iname "readme" -o \
-iname "*.txt" -o \
-iname "*.md" -o \
-iname "*.pdf" -o \
-iname "*.html" -o \
-iname "*.rst" \)
5. find
命令的资料
关于 find 命令的说明,最好的不是网上的某一篇讲述 find 用法的文档,也不是本文,而是 find 自带的 man 手册。
如果你觉得终端查看 man find
的输出不方便,可以将其输出到文本文件中查看。
man find | tee man-find.txt
你或许会说官方的 man find
太枯燥,看不懂。
我的建议是,看不懂可以多看几遍,这次看不懂可以过段时间再看,理由如下:
- 这是
find
用法的第一手资料,养成自己看第一手资料的习惯,不要总是看别人的二手货,包括本篇; - linux 的命令都是这种类型,会阅读
find
的帮助信息,那看其他命令的帮助信息也没啥问题了。
相比于学习 find
命令的某个具体用法,其实使用 man
来查看帮助的习惯更重要。
6. 近期文章
赋能工具
- grep,资深程序员的 grep 笔记
- xxd,十六进制操作神器
Makefile
- Makefile 调试,检查目标和依赖的时间关系
- Makefile 学习,千万别错过这份教程
代码阅读 & 程序调试
- 读代码,没有文档怎么办?
- 读代码,如何精准定位?
- 读代码,没有头绪怎么办?
- 读代码,多用用上帝视角
- 程序查错,一种排查问题的通用方法
- 程序查错,一类常见错误的处理思路
- 程序查错,第一件事要做什么?
其它
- 两步操作在 C 代码中集成 GoogleTest 框架
7. 其它
洛奇自己维护了一个公众号“洛奇看世界”,不定期瞎逼逼。公号也提供个人联系方式,一些资源,说不定会有意外的收获,详细内容见公号提示。扫下方二维码关注公众号: