基础匹配
正则表达式(regex)是一种描述字符串集合的模式。grep、sed 和 awk 等工具使用正则表达式来搜索和转换文本。字面字符串是最简单的模式:它在行中匹配自身。
grep 'hello' file.txtgrep -i 'error' /var/log/syslog
将模式放在单引号中,使 Shell 原样传递给工具而不做解释。
正则表达式方言
日常使用中涉及三种方言,同一模式在不同方言中行为可能不同。
基本正则表达式(BRE) 是 grep 和 sed 的默认模式。字符 +、?、{、}、(、) 和 | 按字面匹配;需要反斜杠才能获得特殊含义。
扩展正则表达式(ERE) 使这些字符无需转义即具有特殊含义。使用 -E 标志启用 ERE;awk 默认使用 ERE。
grep -E 'error|warning' logfilesed -E 's/[0-9]+/NUM/g' data.txt
Perl 兼容正则表达式(PCRE) 增加了 \d 等简写、环视和惰性量词。在可用的地方使用 grep -P。
grep -P '\d{3}-\d{4}' contacts.txt
如果包含 +、? 或交替的模式静默匹配不到任何内容,检查工具是否运行在 BRE 模式下。
锚点
锚点匹配位置而非字符。
| 模式 | 说明 |
|---|
^ | 行首 |
$ | 行尾 |
\b | 单词边界 |
\B | 非单词边界 |
\< | 单词开头(GNU) |
\> | 单词结尾(GNU) |
grep '^#' config.txtgrep '\.conf$' filelist.txt
组合两个锚点以匹配整行。以下查找仅由数字组成的行。
grep -E '^[0-9]+$' data.txt
单词边界匹配 "cat" 但不匹配 "catalog"。
grep '\bcat\b' animals.txt
字符类
字符类匹配定义集合中的一个字符。
| 模式 | 说明 |
|---|
. | 任意单个字符(除换行符外) |
[abc] | a、b 或 c 中的一个 |
[^abc] | 除 a、b、c 外的任意字符 |
[a-z] | 任意小写字母 |
[A-Z] | 任意大写字母 |
[0-9] | 任意数字 |
[a-zA-Z0-9] | 任意字母数字字符 |
\d | 任意数字,同 [0-9](仅 PCRE) |
\D | 任意非数字(仅 PCRE) |
\w | 任意单词字符(字母、数字、下划线) |
\W | 任意非单词字符 |
\s | 任意空白字符(空格、制表符、换行) |
\S | 任意非空白字符 |
GNU grep 和 sed 即使不带 -P 也接受 \w、\s 和 \b,但 \d 仅在 PCRE 中有效。为可移植脚本,使用 POSIX 字符类。
在方括号表达式内部,大多数元字符变为字面字符。要包含字面 ],将其放在首位;字面 - 放在首位或末位。
POSIX 字符类
POSIX 字符类在所有 Unix 工具中可移植。它们写在方括号表达式内部,因此你会看到双层方括号。
| 类 | 说明 |
|---|
[[:alpha:]] | 字母字符 |
[[:digit:]] | 数字(0-9) |
[[:alnum:]] | 字母数字字符 |
[[:space:]] | 空白字符 |
[[:upper:]] | 大写字母 |
[[:lower:]] | 小写字母 |
[[:punct:]] | 标点字符 |
[[:print:]] | 可打印字符 |
[[:blank:]] | 空格和制表符 |
[[:xdigit:]] | 十六进制数字 |
grep '[[:digit:]]' data.txttr -d '[:punct:]' < file.txt
量词
量词控制前面的元素必须出现的次数。
| 模式 | 说明 |
|---|
* | 零次或多次 |
+ | 一次或多次 |
? | 零次或一次 |
{n} | 恰好 n 次 |
{n,} | n 次或更多 |
{n,m} | n 到 m 次之间 |
grep -E 'o{2,}' words.txtgrep -E 'https?://' urls.txt
量词是贪婪的:它们尽可能多地匹配文本。星号在任何方言中无需转义即可工作;在 BRE 中,区间写作 \{n,m\},GNU 工具也接受 \+ 和 \?。
分组与交替
圆括号将元素分组,使量词应用于整个单元,并捕获匹配的内容。管道符号提供交替选择。
| 模式 | 说明 |
|---|
(abc) | 分组:将 "abc" 作为单元匹配 |
a\|b | 交替:匹配 a 或 b |
\1 | 反向引用:再次匹配第一个捕获组 |
\2 | 反向引用:再次匹配第二个捕获组 |
grep -E 'cat|dog' animals.txtecho 'abcabc' | grep -E '(abc)\1'
反向引用在 sed 中大放异彩,可以在替换中重新排列匹配到的文本。
echo 'John Smith' | sed -E 's/(.*) (.*)/\2, \1/'
转义
反斜杠移除元字符的特殊含义。在扩展正则中,以下字符是特殊的:
$ . * + ? ( ) [ ] { } | ^ $ \
要字面匹配其中之一,在其前面加反斜杠。没有转义时,下面的点会匹配任意字符,因此该模式也会匹配 "192x168y1z1"。
grep '192\.168\.1\.1' hosts.txt
环视
PCRE 支持环视:零宽断言,约束匹配周围的上下文而不将其包含在匹配中。还增加了惰性量词。
| 模式 | 说明 |
|---|
(?=x) | 正向前瞻:后面跟着 x |
(?!x) | 负向前瞻:后面不跟着 x |
(?<=x) | 正向后顾:前面是 x |
(?<!x) | 负向后顾:前面不是 x |
*?+? | 惰性量词:尽可能少地匹配 |
仅当 "bar" 不跟随时匹配 "foo"。
grep -P 'foo(?!bar)' file.txt
配合 -o 仅打印匹配部分,后顾可以提取值而不包含其标签。
grep -oP '(?<=user=)\w+' auth.log
常见示例
匹配看起来像邮箱地址的行。
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' contacts.txt
匹配 IPv4 地址。这种宽松形式也接受大于 255 的数字,通常对日志搜索来说足够了。
grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' logfile
仅打印匹配部分而非整行。
grep -oE '[0-9]+' file.txt
删除文件中的空行。
删除注释行,包括带缩进的注释。
sed '/^[[:blank:]]*#/d' config.txt
将连续空白替换为单个空格。
sed -E 's/[[:blank:]]+/ /g' messy.txt
打印第一个字段是数字的行。
awk '$1 ~ /^[0-9]+$/' data.txt