一份资深程序员的 find 笔记

6 篇文章 0 订阅
5 篇文章 3 订阅

本文原创发布于微信公众号“洛奇看世界”

我之前分享过一篇《一份资深程序员的 grep 笔记》,现在分享一篇我的 find 笔记。

find 命令很复杂,一开始总是记不住如何使用,后来每次使用时就把没见过的用法记下来,最后形成了这篇笔记的主体。

find 命令很复杂,虽然我已经用过几百甚至上千遍了,但时间长了还是经常会忘记具体的用法,这时候我就会回到笔记中快速找下该如何使用,因为是完全按照自己的习惯记录的笔记,所以回忆起来也很快,实在想不起了,就照着用法 copy 一下,哈哈。

下面是本文的目录内容,可以根据需要阅读:

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
  1. -path-prune 配合使用,排除满足 “*stbgcc-6.3-1.8*” 和 “*linuxkernel.aarch64-linux.97271.dbg*” 模式的路径;
  2. 多个 “! -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,不再需要往下查找了,所以使用:

  1. -print 选项打印,
  2. 然后使用 -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 太枯燥,看不懂。

我的建议是,看不懂可以多看几遍,这次看不懂可以过段时间再看,理由如下:

  1. 这是 find 用法的第一手资料,养成自己看第一手资料的习惯,不要总是看别人的二手货,包括本篇;
  2. linux 的命令都是这种类型,会阅读 find 的帮助信息,那看其他命令的帮助信息也没啥问题了。

相比于学习 find 命令的某个具体用法,其实使用 man 来查看帮助的习惯更重要。

6. 近期文章

赋能工具

  • grep,资深程序员的 grep 笔记
  • xxd,十六进制操作神器

Makefile

  • Makefile 调试,检查目标和依赖的时间关系
  • Makefile 学习,千万别错过这份教程

代码阅读 & 程序调试

  • 读代码,没有文档怎么办?
  • 读代码,如何精准定位?
  • 读代码,没有头绪怎么办?
  • 读代码,多用用上帝视角
  • 程序查错,一种排查问题的通用方法
  • 程序查错,一类常见错误的处理思路
  • 程序查错,第一件事要做什么?

其它

  • 两步操作在 C 代码中集成 GoogleTest 框架

7. 其它

洛奇自己维护了一个公众号“洛奇看世界”,不定期瞎逼逼。公号也提供个人联系方式,一些资源,说不定会有意外的收获,详细内容见公号提示。扫下方二维码关注公众号:

公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洛奇看世界

一分也是爱~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值