Linux下命令行(三)之文本处理高级工具
【正则表达式】
正则表达式是用来描述一系列字符串的简洁的符号组合。构成一个正则表达式的基本元素有:
普通字符:字母、数字、下划线、空格等等
元字符:有特殊含义的字符或字符串
正则表达式元字符及其优先级:
元字符类别 | 作用 | 示例 |
圆括号 | 分组或捕获 | (...) (?:...) (?<LABEL>...) |
量词 | 用于修饰 | a* a+ a? a{m,n} |
锚位 | 用于定位 | ^a a$ |
序列 | 由普通字符组成 | abc d2 |
择一 | 选择一个序 | a|(bc)|d |
元素 | 构成正则表达式的元素 | a [abc] \d \s \w \1 |
注意:优先级从上到下依次减小,但其中锚位和序列的优先级是相同的。
【sed 基础】
流式编辑器 sed 是作为 shell 程序的编辑器而开发的。它是流式化的,输入被读取后,在内部修改,修改后的内容被打印出来,而输入文件并没有被真正改变。它的语法格式如下:
sed 'script' files
其中,参数 script 是能够被 sed 理解的一些命令(可以是系统中的命令,也可以是自己写的脚本命令);参数 files 是输入文件。其中,参数 script 两边的单引号是为了防止 shell 进行通配符匹配或者额外的置换操作。
如果没有给出需要处理的文件,则 sed 将从标准输入中读取字符流。这使得 sed 可以用于文本过滤。sed 命令运行的执行的操作如下所示:
(1)从一个输入文件中读取一行;
(2)做一个这一行的拷贝;
(3)对这个拷贝行进行处理,即执行参数 script 指定的程序;
(4)到下一行,从第 1 步开始重复流程,直到文本结束;
参数 script 通常由下列形式的一行或多行组成:
/pattern/ action
其中, pattern 就是一个正则表达式,而 action 是 sed 遇到 pattern 时才去执行的操作。可以指定多重 pattern 和 action 对。当 script 被执行时,它对每个记录进行如下操作:
(1)每个 pattern 被持续地搜索,直到找到一个匹配或者到达记录结尾;
(2)当一个匹配被发现时,就对该记录执行相应的 action 动作;
(3)当 action 完成时,下一个 pattern 将会被使用并重复第 1 步;
(4)当所有的 pattern 都完成时,下一行将被读取,然后重复第 1 步;
常用的action动作如下:
操作 | 描述 |
p | 打印行 |
d | 删除行 |
s | 用一个表达式置换另一个表达式 |
如果缺省了 pattern,则对每一行都执行 action 操作。
需要注意的是,在使用 p 动作时,默认的 sed 属性是把它处理的每一行都打印出来,可以使用选项 n 来禁止 sed 的这个动作。语法格式如下:
sed -n 'script' files
对于替换动作 s 的使用方式如下:
/pattern/s/pattern1/pattern2/
它 的含义是,对于每一个匹配 pattern 的行,将其中匹配 pattern1 的部分替换成 pattern2 代表的字符序列。但是,在替换操作的使用中,我们发现 pattern 与 pattern1 都用于检索匹配字符序列,所以通常会忽略 pattern ,更加常用的s动作的形式是:
s/pattern1/pattern2/g
它的含义就是将每一行中匹配 pattern1 的部分替换成 pattern2 代表的字符序列。其中,修饰符 g 告诉 sed 进行全局查找替换操作,默认行为是只替换一行中第一个出现匹配的字符序列。当然,也可以不使用这个修饰符。其它修饰符及其作用如下:
g 全局查找替换
i 匹配时忽略字母的大小写
s 使得元字符.在匹配时可以匹配任意一个字符,包括换行符
另外,你可以通过选项 e 来多次执行 sed 操作,如下:
sed -e 'script1' ... -e 'scriptN' files
相当于执行了 N 次 sed 操作。
【awk 基础】
awk 是一个能让你按照匹配来搜索许多文件并有条件地修改文件的工具,实际上它还是一门完整的编程语言。它的基本语法如下:
awk 'script' files
其中,参数 files 是一个文件列表,表示需要处理的文件。参数 script 是如下形式的一个或多个命令的集合:
/pattern/ {actions}
这里的 pattern 是一个正则表达式,actions 是后面将要提到的能被 awk 理解的命令。如果 pattern 被省略了,则 awk 将对输入的每一行进行处理。
awk 最有用的功能是把它的输入分成字段。一个字段是一个字符集合,被一个或多个字段按照分隔符分割开来。默认的字段分隔符是空白符(包括空格符和制表符)。
当
一行被读入时,awk 按照分隔符把这一行解析为多个字段,然后把第一个字段放在变量 1 中,第二个字段放在变量 2 中,以此类推,而变量 0
存储整个行的内容。你可以通过操作符 $ 来提取字段变量的值,如 $1 表示第一个字段的内容。所以,下面的命令让你打印 passwd
文件中的用户名及其 UID :
awk -F: '{print $1, $3}' /etc/passwd
在这个命令中,缺省了 pattern 模式匹配。
awk 作为一门编程语言,它有自己的语法元素:
比较操作符
在 awk 中比较操作符可以用于比较数字和字符串的值,当使用比较操作符时,awk 命令的形式如下所示:
awk 'expression {actions}' files
这里的 expression 由下列比较操作符构成:
操作符 | 含义 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
value ~ /pattern/ | 如果 value 匹配模式 pattern ,则为真 |
value !~ /pattern/ | 如果 value 不匹配模式 pattern ,则为真 |
正因为有比较操作符,才可以根据每一行中的某个字段的值进行操作。比如下面的命令:
awk -F: '$3 > 500 { print $1, $3 }' /etc/passwd
这里输出所有 UID 高于 500 的用户的用户名及其 UID 。
你可以在 expression 中使用混合表达式,如下所示:
(expr1) || (expr2)
(expr1) && (expr2)
分别表示或和与的含义。比如下面的语句:
awk -F: '($1 ~ /^[fF]lex$/) && ($3 > 500) { print $1, $3 }' /etc/passwd
将打印用户名 Flex 或者 flex 且 UID 大于 500 的用户的用户名和 UID 。
变量
awk 中的变量与 shell 中的变量非常类似:它们都是被赋有一个值的单词。基本的定义变量的语法如下:
name=value
其中,name 是变量名,value 是变量的值。就像 shell 中的变量一样,变量名只能包含字母、数字和下划线,且不能以数字开头。另外,在引用变量的值时,不需要使用 $ 符号,当然前面提到的字段变量是例外。如下:
fruit=peach
fruity=fruit
结果是,变量 fruit 和 fruity 的值都是 peach 。
如果变量的值是数字,你还可以对这些变量进行数值计算,采用的如下形式:
num1 operator num2
awk 中的数值操作符如下所示:
operator | 描述 |
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取模(求余) |
^ | 求幂 |
在数值计算中,如果操作数不是数值,而是字符串,则它们的值被 0 替代。例如下面的脚本:
#!/bin/sh for i in $@ do if [ -f $i ] ; then echo $i awk '/^ *$/ { x=x+1; print x; }' $i # x 一开始是空值,能够执行 x=x+1 操作的原因就是等号右边的x被0替代了 else echo "ERROR: $i not a file" > &2 fi done
流程控制结构
if 语句的格式如下:
awk '{ if (expr1) { action1 } else if (expr2) { # 这里是 else if,shell中是 elif,注意区别 action2 } else { action3 } }' files
通常,在 expr 中使用匹配判断。
类似,while 结构如下:
awk '{ while (expr) { action } }' files
另外,还有 do ... while 结构的循环:
awk '{ do { action } while (expr) }' files
最后,for 循环的结构如下:
awk '{ for (init; test; incr) { action } }' files
应该发现,awk 中使用的几种控制结构如 C 中的控制结构是完全相同的。
最后,需要补充的一点是,awk 中默认的字段分隔符是空白符,你可以通过选项 F 来改变字段分隔符,就像上面很多例子中用的那样。