#《快乐的 Linux 命令行》

#grep

1
grep [options] regex [file...]

选项列表:

选项 描述
-i 忽略大小写,不会区分大小写字符。也可用--ignore-case来指定。
-v 不匹配。通常,grep程序会打印包含匹配项的文本行。这个选项导致grep程序只会打印不包含匹配项的文本行。也可用--invert-match来指定。
-c 打印匹配的数量(或者是不匹配的数目,若指定了-v选项),而不是文本行本身。也可用--count选项来指定。
-l 打印包含匹配项的文件名,而不是文本行本身,也可用--files-with-matches选项来指定。
-L 相似于-l选项,但是只是打印不包含匹配项的文件名。也可用--files-without-match来指定。
-n 在每个匹配行之前打印出其位于文件中的相应行号。也可用--line-number选项来指定。
-h 应用于多文件搜索,不输出文件名。也可用--no-filename选项来指定。

#测试

1
2
3
4
5
6
$ ls /bin > dirlist-bin.txt
$ ls /usr/bin > dirlist-usr-bin.txt
$ ls /sbin > dirlist-sbin.txt
$ ls /usr/sbin > dirlist-usr-sbin.txt
$ ls dirlist*.txt
dirlist-bin.txt dirlist-sbin.txt dirlist-usr-bin.txt dirlist-usr-sbin.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 搜索 bzip
$ grep bzip dirlist*.txt
dirlist-bin.txt:bzip2
dirlist-bin.txt:bzip2recover

# 搜索 bzip 对应的文件
$ grep -l bzip dirlist*.txt
dirlist-bin.txt

# 搜索不包含 bzip 的文件
$ grep -L bzip dirlist*.txt
dirlist-sbin.txt
dirlist-usr-bin.txt
dirlist-usr-sbin.txt

# 圆点 . 匹配任意字符
$ grep -h '.zip' dirlist*.txt
bunzip2
bzip2
bzip2recover
gunzip
gzip
funzip
gpg-zip
mzip
preunzip
prezip
prezip-bin
unzip
unzipsfx

# 插入符合 ^ 锚点开头
$ grep -h '^zip' dirlist*.txt
zip
zipcloak
zipdetails
zipgrep
zipinfo
zipnote
zipsplit

# 美元符合 $ 锚点结尾
$ grep -h 'zip$' dirlist*.txt
gunzip
gzip
funzip
gpg-zip
mzip
preunzip
prezip
unzip
zip

$ grep -h '^zip$' dirlist*.txt
zip

# 第三个字母是j 第五个字母是r 一共五个字母
grep -i '^..j.r$' /usr/share/dict/words
Major
major

/usr/share/dict/目录下有个英文字典。

中括号表达式中使用元字符,插入字符^被用来表示否定,连字符字符-被用来表示一个字符范围。

一个否定的字符集仍然在给定位置要求一个字符,但是这个字符必须不是否定字符集的成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ grep -h '.zip' dirlist*.txt | wc -l
13
$ grep -h '[bg]zip' dirlist*.txt | wc -l
3
$ grep -h '[^bg]zip' dirlist*.txt | wc -l
10
$ grep -h '[^bg]zip' dirlist*.txt
bunzip2
gunzip
funzip
gpg-zip
mzip
preunzip
prezip
prezip-bin
unzip
unzipsfx
# 可以看到 zip 这个文件没在里面,因为前面要有一个字符,但这个字符不能是 b 或 g
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ grep -h '^[ABCDEFGHIJKLMNOPQRSTUVWXZY]' dirlist*.txt
GET
HEAD
POST
X
X11
Xephyr
Xorg
Xwayland
ModemManager
NetworkManager

# 等价于👇
$ grep -h '^[A-Z]' dirlist*.txt
GET
HEAD
POST
X
X11
Xephyr
Xorg
Xwayland
ModemManager
NetworkManager

# 可以匹配多个范围
$ grep -h '^[A-Za-z0-9]' dirlist*.txt | wc -l
2344

如果正则表达式中需要包含连字符-,让它成为表达式的第一个字符:

1
2
# 会匹配包含一个连字符“-”,或一个大写字母“A”,或一个大写字母“Z”的文件名。
grep -h '[-AZ]' dirlist*.txt

POSIX把正则表达式的实现分成了两类:基本正则表达式(BRE)和扩展的正则表达式(ERE)。

BRE的元素集合:

1
^ $ . [ ] *

ERE额外添加的元字符:

1
( ) { } ? + |

|代表交替:

1
2
3
4
5
6

$ echo "AAA" | grep -E 'AAA|BBB'
AAA
$ echo "BBB" | grep -E 'AAA|BBB'
BBB
$ echo "CCC" | grep -E 'AAA|BBB'

?匹配零个或一个元素,表示前面的字符可有可无:

1
2
3
4
$ echo "(555) 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
(555) 123-4567
$ echo "555 123-4567" | grep -E '^\(?[0-9][0-9][0-9]\)? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$'
555 123-4567

*匹配零个或多个元素。
+匹配一个或多个元素。
{}匹配特定个数的元素:

选项 描述
n 匹配前面的元素,如果它确切地出现了n
n,m 匹配前面的元素,如果它至少出现了n次,但是不多于m
n, 匹配前面的元素,如果它出现了n次或多于n
,m 匹配前面的元素,如果它出现的次数不多于m
1
2
$ echo "(555) 123-4567" | grep -E '^\(?[0-9]{3}\)? [0-9]{3}-[0-9]{4}$'
(555) 123-4567

生成电话号码文件:

1
for i in {1..10}; do echo "(${RANDOM:0:3}) ${RANDOM:0:3}-${RANDOM:0:4}" >> phonelist.txt; done

查找有问题的项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ cat phonelist.txt
(156) 891-573
(551) 310-2301
(197) 501-1688
(108) 737-3818
(822) 225-1332
(13) 200-1923
(137) 105-1455
(391) 180-2722
(317) 546-2211
(172) 279-9606
$ grep -Ev '^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$' phonelist.txt
(156) 891-573
(13) 200-1923

273/505