在Linux find命令中正则表达式的各种用法
在本教程中,我们将讨论将命令 find 与正则表达式(regex)一起使用。我们将了解如何指定正则表达式以进一步优化搜索结果。
正则表达式
在介绍如何在 find 中使用正则表达式之前,我们先来了解一下什么是正则表达式以及正则表达式的构造。
正则表达式(简称 regex)是一种功能强大的工具,由指定搜索模式的字符序列描述。正因为如此,将正则表达式与 find 结合使用,就能以更精简的命令实现更精细的搜索。
正则表达式有不同的类型和格式。下面解释的概念在它们之间是一致的。不过,更高级的功能需要知道使用的是哪种类型的正则表达式,因为它们之间存在差异。下文将详细介绍 find 命令所接受的 regex 类型。
主要正则表达式语法和示例
虽然 regex 有时会让人望而生畏,但它能改进搜索并增强与命令行的交互。只需掌握基本知识,我们就能从中获益。
简单介绍一下,regex 标记可以匹配多个字符:
- 句号(.):匹配任何字符一次(换行符除外):q.e 将匹配字符串 qwe、qre 和 qee,但不匹配字符串 qe 或 qwwe。
- 星号(* ):匹配前一个字符/正则表达式的零次或多次出现:qw*e 将匹配字符串 qe、qwe 和 qwwe,但不匹配字符串 qre。
- 反斜线 (\):用于转义特殊字符,例如,用于搜索句号:q\.e 将匹配字符串 q.e,但不匹配字符串 qre、qee、qe 或 qwwe。
- 方括号([字符串]):限定只能匹配方括号内字符,任何一个字符可以:q[we]r 将匹配字符串 qwr 和 qer,但不匹配字符串 qr、qwer 或 qwewer。
- 尖括号 (^):它排除方括号内的内容(尽管在文件中搜索时它也指定行的开头):q[^we]r 将匹配字符串 qar 和 qsr,但不匹配字符串 qwr 或 qer。
根据前面的讨论,.* 将匹配除换行符之外的任何字符的零次或多次出现,这意味着它将匹配任何字符串!
命令描述
find命令的使用可分为两个部分:path和expression:
find [path] [expression]
path是搜索的目录。expression部分还包括在符合搜索条件的文件中可能采取的操作。在这里,find命令有三个与正则表达式相关的选项。我们现在通过一些用例示例来展示它们。以下模型目录将用于示例:
$ tree ./
./
├── a0
├── a0.sh
├── A0.sh
├── a1
├── a1.sh
├── A1.sh
├── a2
├── ca
├── cb
├── cc
└── folder
├── a0
├── a1
└── a0folder
├── a0
└── a1
使用 -regex
第一推荐是使用-regex参数指定正则表达式:
find [path] -regex [regular_expression]
使用该命令将搜索目录文件,并返回符合 regular_expression 的文件。regular_expression 搜索目标涵盖了完整的文件名,包括根目录。这意味着,如果在当前目录下查找,正则表达式应以 \.\/ 开头(使用反斜杠转义特殊字符)。
下面的命令查找当前目录(\.\/)中以字母 a 开头、后跟 0 或 1 的文件(使用 -type f 标志):
find ./ -type f -regex '\.\/a[01].*' ./a1 ./a0 ./a1.sh ./a0.sh
文件 a2 没有返回,因为字母 a 后面没有 0 或 1。 我们还可以使用命令在一级目录中搜索,而不是在当前目录中搜索:
find ./ -type f -regex '\.\/[^/]*\/a[01][^/]*' ./folder/a1 ./folder/a0
最后两个 regexes 有两个不同之处。首先,标记[^/]*\/指的是任何不包含任何斜线([^/]*)的字符串,后面紧接着以字母 a 开头的文件名前的一个斜线(\/)。其次,我们用 [^/]替换了句号,表示字母 a 后面不能再出现斜线。
子目录中的文件不符合 regex:在第一个斜线(当前目录)和紧跟字母 a 的斜线之间有额外的斜线表示子目录(例如 ./folder/a0folder/a0)。
最后,要包含所有子目录中的所有文件,我们可以使用
find ./ -type f -regex '.*a[01].*' ./folder/a0folder ./folder/a0folder/a0 ./folder/a0folder/a1 ./folder/a0 ./folder/a1 ./a0 ./a1 ./a0.sh ./a1.sh
使用 -iregex
第二种是-iregex:
find [path] -iregex [regular_expression]
该命令执行与 -regex 选项相同的搜索,但忽略搜索模式的字母大小写。为了便于记忆,命令 -iregex 代表不区分大小写的 regex。
如果我们修改之前的一条命令,只查找带点的文件(包括[.]),输出结果如下:
find ./ -type f -regex '\.\/a[01][.].*' ./a0.sh ./a1.sh
使用 -iregex 标志而不是 -regex 标志得到的结果也包括大写字母 A 的文件:
find ./ -type f -iregex '\.\/a[01][.].*' ./a0.sh ./A1.sh ./A0.sh ./a1.sh
使用 -regextype
最后,选项 -regextype 用来选择正则表达式的类别:
find [path] -regex [regular_expression] -regextype [regex_type]
find命令有不同的 regex 类型:
- findutils
- emacs (是默认选项,除非另有说明)
- gnu-awk
- grep
- egrep
- posix-awk
- posix-basic
- posix-egrep
- posix-extended
前面定义的表达式与所有这些类型的 regex 都兼容。不过,在不同的 regex 类型下,更高级的搜索查询可能会产生不同的结果。有一个全面的 GNU 网页专门详细介绍了不同的语法。
与 Bash Globbing 的对比
在使用 Linux 一段时间后,bash 的 globbing 功能肯定会出现在 ls 等命令中。让我们看看下面这条命令:
ls *.png
它会列出扩展名为 .png 的所有文件。同时,命令:
ls M*.png
列出了扩展名为 .png 并以字母 M 开头的所有文件。这就是 Bash globbing 的作用:文件名补全。使用 find 命令搜索文件名时,会用到 Bash globbing 。
尽管它们看起来很相似,但 bash globbing 和正则表达式的语法却不尽相同,这使得问题变得更加复杂。我们将讨论其中两个最相关的差异。在 bash 中,句点(.)代表一个字面意义上的句点,而在 regex 中,句点代表任何一个字符。第一条命令展示了 bash globbing :
find ./ -type f -name 'a*.sh' ./a0.sh ./a1.sh
要获得相同的结果,我们可以使用下面的 regex 查找命令:
find ./ -type f -regex '\.\/a.*\.sh' ./a0.sh ./a1.sh
bash globbing 与正则表达式的另一个区别是星号 (*):在 bash globbing 中,星号代表零个或多个任意字符,但在 regex 中,星号代表零个或多个前一个字符。因此,无论是 bash globbing 还是 regex,类似命令的行为都是不同的。当我们使用 bash globbing 时,下面的命令会返回所有以 c 开头的文件:
find ./ -type f -name 'c*' ./cb ./cc ./ca
但是,如果使用 regex 类似的搜索模式,则会返回名称中只包含 c 的所有文件:
find ./ -type f -regex '\.\/c*' ./cc
在目录中搜索时,我们应该牢记这些差异,以便更好地使用 bash 语法或正则表达式。