九:Shell 文本处理工具与正则表达式¶
本文已基本完稿,正在审阅和修订中,不是正式版本。
导言
本章节将介绍一些常用的 shell 文本处理工具,它们可以帮助你更加得心应手地处理大量有规律的文本。
为保持简介,各工具的介绍皆点到即止,进一步的用法请自行查找。
一些铺垫¶
I/O 重定向和管道¶
在 六:网络下载工具与 Shell 脚本 中提到过:
Bash 支持 I/O 重定向(>, >>, <)和管道(|)等
下面简单介绍重定向和管道,为后续知识做铺垫
重定向¶
一般情况下命令从标准输入(stdin)读取输入,并输出到标准输出(stdout),默认情况下两者都是你的终端。使用重定向可以让命令从文件读取输入/输出到文件。下面是以 echo
为例的重定向输出:
$ echo "Hello Linux!" > output_file
$ cat output_file
Hello Linux!
$ echo "rewrite it" > output_file
$ cat output_file
rewrite it
$ echo "append it" >> output_file
$ cat output_file
rewrite it
append it
无论是 >
还是 >>
,当输出文件不存在时都会创建该文件
重定向输入使用符号 <
:
command < inputfile
command < inpufile > outputfile
小知识
除了 stdin 和 stdout 还有标准错误(stderr),0、1、2分别是他们的编号。stderr 可以用 2>
重定向。注意数字 2 和 > 之前没有空格。
使用 2>&1
可以将 stderr 合并到 stdout。
思考题
wc -l file
和 wc -l < file
输出有什么区别?为什么?
echo < file
会输出什么?
管道¶
管道(pipe),操作符 |
,作用为将符号左边的命令的 stdout 接到之后的命令的 stdin。管道不会处理 stderr。
$ ls / | grep bin
bin
sbin
单双引号¶
当你需要输入一个含有空白字符的字符串的时候,加上引号可以避免它被切分。
在 bash 中,单引号意为“强引用”,即引号之间的特殊字符会被认为是普通字符,两个单引号之间不能再有单引号,即使使用‘\’进行转义。而双引号意为”弱引用“,'$', '`', '\', '!' 在双引号之间有特殊含义。参考 Bash Quoting
常用 shell 文字处理工具¶
diff¶
diff 工具用于比较两个文件的不同,并列出差异。
$ echo hello > file1
$ echo hallo > file2
$ diff file1 file1
$ diff file1 file2
1c1
< hello
---
> hallo
小知识
加参数 -w
可忽略所有空白字符, -b
可忽略空白字符的数量变化。
假如比较的是两个文本文件,差异之处会被列出;假如比较的是二进制文件,只会指出是否有差异。
head & tail¶
顾名思义,head 和 tail 分别用来显示开头和结尾指定数量的文字。
以 head 为例的共同用法:
- 不加参数的时候默认显示前 10 行
-n [NUM]
指定行数,可简化为-[NUM]
-c [NUM]
指定字节数-n +[NUM]
输出从开头到倒数第 [NUM] 行,tail 可简化为+[NUM]
-c +[NUM]
输出从开头到倒数第 [NUM] 字节
head file
head -n 25 file
head -25 file
head -c 20 file
head -10 file
tail -10 file
head -n +10 file
tail +10 file
可以简化记忆为 -
表示想要多少(只要前 10 行 / 后 10 行),+
表示“不想要”多少(除了最后 10 行 / 最前 10 行都要)。
tail 比 head 多出的用法:
-f
当文件末尾内容增长时,持续输出末尾增加的内容
tail -f file
常用于动态显示 log 文件的更新
head 和 tail 组合使用:
head -20 file | tail -5
head -20 file | tail +5
sort¶
sort 用于文本的行排序。默认排序方式是升序,按每行的字典序排序。
一些基本用法:
-r
变成降序-u
去除重复行-o [file]
指定输出文件-n
用于数值排序,否则“15”会排在“2”前
$ echo -e "snake\nfox\nfish\ncat\nfish\ndog" > animals
$ sort animals
cat
dog
fish
fish
fox
snake
$ sort -r animals
snake
fox
fish
fish
dog
cat
$ sort -u animals
cat
dog
fish
fox
snake
$ sort -u animals -o animals
$ cat animals
cat
dog
fish
fox
snake
$ echo -e "1\n2\n15\n3\n4" > numbers
$ sort numbers
1
15
2
3
4
$ sort -n numbers
1
2
3
4
15
小知识
为什么有必要存在 -o
参数?试试重定向输出到原文件。
uniq¶
uniq 也可以用来排除重复的行,但是仅对连续的重复行生效。
通常会和 sort 一起使用:
sort animals | uniq
只是去重排序明明可以用 sort -u
,uniq 工具是否多余了呢?实际上 uniq 还有其他用途。
uniq -d
可以用于仅输出重复行:
sort animals | uniq -d
uniq -c
可以用于统计各行重复次数:
sort animals | uniq -c
正则表达式¶
正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
此处仅简单介绍正则表达式的一些用法,对正则表达式有更多兴趣,请移步补充内容。
特殊字符¶
特殊字符 | 描述 |
---|---|
[] | 方括号表达式,表示匹配的字符集合,例如[0-9],[abcde] |
() | 标记子表达式起止位置 |
* | 匹配前面的子表达式零或多次 |
+ | 匹配前面的子表达式一或多次 |
? | 匹配前面的子表达式零或一次 |
\ | 转义字符,除了常用转义外,还有:"\b"匹配单词边界";\B"匹配非单词边界等 |
. | 匹配除“\n"外的任意单个字符 |
{} | 标记限定符表达式的起止。例如{n}表示匹配前一子表达式n次;{n,}匹配至少n次;{n,m}匹配n至m次 |
| | 表明前后两项二选一 |
$ | 匹配字符串的结尾 |
^ | 匹配字符串的开头,在方括号表达式中表示不接受该方括号表达式中的字符集合 |
以上特殊字符,若是想要匹配特殊字符本身,需要在之前加上转义字符“\”
简单示例¶
匹配正整数:
[1-9][0-9]*
匹配仅由 26 个英文字母组成的字符串:
^[A-Za-z]+$
匹配 Chapter 1-99 或 Section 1-99
^(Chapter|Section) [1-9][0-9]{0,1}$
匹配“ter”结尾的单词:
ter\b
基本/扩展正则表达式¶
基本正则表达式(Basic Regular Expressions)和扩展正则表达式(Extended Regular Expressions)是两种 POSIX 正则表达式风格。
BRE 可能是如今最老的正则风格了,对于部分特殊字符(如 +
, ?
, |
, {
)需要加上转义符 \
才能表达其特殊含义。
ERE 与如今的现代正则风格较为一致,相比 BRE,上述特殊字符默认发挥特殊作用,加上 \
之后表达普通含义。
具体的例子在下文介绍工具时可以看到。
思考题
什么是 DFA/NFA 正则表达式引擎?如今常见编程语言里的正则表达式实现和此处的 BRE/ERE 有什么异同?
常用 Shell 文字处理工具(正则)¶
grep¶
grep 全称 Global Regular Expression Print,是一个强大的文本搜索工具,可以在一个或多个文件中搜索指定 pattern 并显示相关行。
grep 默认使用 BRE,要使用 ERE 可以 grep -E 或 egrep。
命令格式:grep [option] pattern file
一些用法:
- -n 显示匹配到内容的行号
- -v 显示不被匹配到的行
- -i 忽略字符大小写
ls /bin | grep -n "^man$"
ls /bin | grep -v "[a-z]\|[0-9]"
ls /bin | grep -iv "[A-Z]\|[0-9]"
sed¶
sed 全称 Stream EDitor,即流编辑器,可以方便地对文件的内容进行逐行处理。
sed 默认使用 BRE,要使用 ERE 可以 sed -E。
命令格式:
sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)
此处的“commond”和“scriptfile”中的命令均指的是 sed 命令。
常见 sed 命令:
- s 替换
- d 删除
- c 选定行改成新文本
- a 当前行下插入文本
- i 当前行上插入文本
echo -e "seD\nIS\ngOod" > sed_demo
sed "2d" sed_demo
sed "2d" sed_demo
sed "s/[a-z]/~/g" sed_demo
sed "3cpErfeCt" sed_demo
awk¶
awk 是一种用于处理文本的编程语言工具,名字来源于三个作者的首字母。相比 sed,awk 可以在逐行处理的基础上,针对列进行处理。默认的列分隔符号是空格,其他分隔符可以自行指定。
awk 使用 ERE。
命令格式:awk [options] 'pattern {action}' [file]
awk 逐行处理文本,对符合的 patthern 执行 action。需要注意的是,awk 使用单引号时可以直接用 $
,使用双引号则要用 \$
。
一些示例:
$ cat awk_demo
Beth 4.00 0
Dan 3.75 0
kathy 4.00 10
Mark 5.00 20
Mary 5.50 22
Susie 4.25 18
$ awk '$3 >0 { print $1, $2 * $3 }' awk_demo
kathy 40
Mark 100
Mary 121
Susie 76.5
示例中 $1
,$2
,$3
分别指代本行的第 1,2,3,列。特别地,$0 指代本行。
思考题
如何使用 sed
对示例中文本的第二列数值求和?