findUnix/Linux命令行工具箱中最棒的工具之一。该命令在命令行和shell脚本编写方面都能发挥功效。同catls一样,find也包含大量特性,多数用户都没有发挥出它的最大威力。这则攻略讨论了find的一些常用的查找功能。

2.4.1 预备知识

find命令的工作方式如下:沿着文件层次结构向下遍历,匹配符合条件的文件,执行相应的操作。默认的操作是打印出文件和目录,这也可以使用-print选项来指定。

2.4.2 实战演练

要列出给定目录下所有的文件和子目录,可以采用下面的语法:

  1. $ find base_path

bash_path可以是任意位置(例如/home/slynux),find会从该位置开始向下查找。例如:

  1. $ find . -print
  2. .
  3. ./variables.sh
  4. ./else
  5. ./if.sh
  6. ./showArgs.sh
  7. ./out.txt
  8. ./temp.txt
  9. ./welcome.txt
  10. ./printf.sh
  11. ./output.txt
  12. ./umq
  13. ./input.txt
  14. ./tools
  15. ...

.指定当前目录,..指定父目录。这是Unix文件系统中的约定用法。

print选项使用\n(换行符)分隔输出的每个文件或目录名。而-print0选项则使用空字符'\0'来分隔。-print0的主要用法是将包含换行符或空白字符的文件名传给xargs命令。随后会详细讨论xargs命令:

  1. $ echo "test" > "file name"
  2. $ find . -type f -print | xargs ls -l
  3. ls: cannot access './tools/git-2.33.1/t/t4135/diff-with': No such file or directory
  4. ls: cannot access 'backslash.diff': No such file or directory
  5. $ find . -type f -print0 | xargs -0 ls -l
  6. -rw-r--r-- 1 root root 0 Jan 16 11:04 ./else
  7. -rwxr-xr-x 1 root root 105 Jan 16 11:41 ./if.sh

2.4.3 补充内容

上面的例子演示了如何使用find列出文件层次中所有的文件和目录。find命令能够基于通配符或正则表达式、目录树深度、文件日期、文件类型等条件查找文件。

根据文件名或正则表达式进行搜索

-name选项指定了待查找文件名的模式。这个模式可以是通配符,也可以是正则表达式。在下面的例子中,’*.txt‘能够匹配所有名字以.txt结尾的文件或目录。

注意*.txt两边的单引号。shell会扩展没有引号或是出现在双引号(")中的通配符。单引号能够阻止shell扩展*.txt,使得该字符串能够原封不动地传给find命令。

  1. $ find /home/slynux -name '*.txt' -print
  2. ./out.txt
  3. ./temp.txt
  4. ...
  1. [root@dev workspace]# find . -name '*.txt' -print
  2. ./out.txt
  3. ./temp.txt
  4. ./welcome.txt
  5. ./output.txt
  6. ./input.txt
  7. ...

find命令有一个选项-iname(忽略字母大小写),该选项的作用和-name类似,只不过在匹配名字时会忽略大小写。例如:

  1. $ ls
  2. example.txt EXAMPLE.txt file.txt
  1. $ find . -iname "example*" -print
  2. ./example.txt
  3. ./EXAMPLE.txt

find命令支持逻辑操作符。-a-and选项可以执行逻辑与(AND)操作,-o-or选项可
以执行逻辑或(OR)操作。

  1. $ ls
  2. echo golang input.txt out.txt showArgs.sh tools variables.sh welcome.txt
  3. else if.sh output.txt printf.sh temp.txt umq vitest
  4. $ find . \( -name '*.txt' -o -name '*.sh' \) -print
  5. ./showArgs.sh
  6. ./out.txt
  1. [root@dev workspace]# ls
  2. echo golang input.txt out.txt showArgs.sh tools variables.sh welcome.txt
  3. else if.sh output.txt printf.sh temp.txt umq vitest
  4. [root@dev workspace]# find . \( -name '*.txt' -o -name '*.sh' \) -print
  5. ./variables.sh
  6. ./if.sh
  7. ./showArgs.sh
  8. ./out.txt
  9. ./temp.txt
  10. ./welcome.txt
  11. ./printf.sh
  12. ./output.txt
  13. ./input.txt

上面的命令会打印出所有的.txt.sh文件,因为这个find命令能够匹配所有这两类文件。\(以及\)用于将-name '*.txt' -o -name '*.sh'视为一个整体。

下面的命令演示了如何使用-and操作符选择名字以t开头且其中包含s的文件:

  1. $ find . \( -name '*s*' -and -name 't*' \)
  2. ./tools
  1. [root@dev workspace]# ls
  2. echo golang input.txt out.txt showArgs.sh tools variables.sh welcome.txt
  3. else if.sh output.txt printf.sh temp.txt umq vitest
  4. [root@dev workspace]# find . \( -name '*s*' -and -name 't*' \)
  5. ./tools

-path选项可以限制所匹配文件的路径及名称。例如,$ find /home/users -path '*/slynux/*' -name '*.txt' –print 能够匹配文件/home/users/slynux/readme.txt,但无法匹
/home/users/slynux.txt

-regex选项和-path类似,只不过前者是基于正则表达式来匹配文件路径的。

正则表达式比通配符更复杂,能够更精确地进行模式匹配。使用正则表达式进行文本匹配的一个典型例子就是识别E-mail地址。E-mail地址通常采用name@host.root这种形式,所以可以将其一般化为[a-z0-9]+@[a-z0-9]+\.[a-z0-9]+。中括号中的字符表示的是一个字符组。在这个例子中,该字符组中包含a-z0-9。符号+指明在它之前的字符组中的字符可以出现一次或多次。点号是一个元字符(就像通配符中的?),因此必须使用’\‘对其转义,这样才能匹配到E-mail地址中实际的点号。这个正则表达式可以理解为:一系列字母或数字,然后是一个@,接着是一系列字母和数字,再跟上一个点号,最后以一系列字母和数字结尾。我们会在4.2节中详细讲述正则表达式。

下面的命令可以匹配.txt.sh文件:

  1. $ ls
  2. echo golang input.txt out.txt showArgs.sh tools variables.sh welcome.txt
  3. else if.sh output.txt printf.sh temp.txt umq vitest
  4. $ find . -regex '.*\.(txt\|sh\)$'
  5. ./input.txt
  6. ./showArgs.sh