一、初识sed和gawk
1) 文本处理
1.1) sed编辑器
sed编辑器被称作流编辑器(stream editor),跟普通交互式文本编辑器恰好相反。在交互式文本编辑器中(比如vim),你可以用键盘命令来交互式的插入、删除或替换数据中
的文本。流编辑器则会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。
sed编辑器可以基于输入到命令行的或是存储在命令文本文件中的命令来处理数据流中的数据。它每次从输入中读取一行,用提供的编辑器命令匹配数据、按命令中指定的
方式修改数据流中的数据,然后将生成的数据输出到STDOUT。在流编辑器将所有命令与一行数据进行匹配后,它会读取下一行数据并重复这个过程。在流编辑器处理完流中
所有数据行后,它就会终止。
使用sed命令的格式如下:
sed options script file
选项参数允许你修改sed命令行为,同时它还包含下面列出的选项。
script参数指定了将作用在流数据上的单个命令。如果需要多个命令,你必须用-e选项来在命令行上指定它们,或用-f选项来在单独的文件中指定。
1.1.1) 在命令行定义编辑器命令
例如:
又例如:
1.1.2) 在命令行使用多个编辑器命令
要在sed命令行上执行多个命令时,只要用-e选项就可以了。命令之间必须用分号隔开,并且在命令末尾和分号之间不能有空格。
例如:
又如:
1.1.3) 从文件中读取编辑器命令
2) gawk程序
gawk程序是unix中原始awk程序的GNU版本。gawk程序让流编辑器迈上了一个新台阶,它提供了一种编程语言而不只是编辑器命令。在gawk编程语言中,你可以做下面的事情。
- 定义变量来保存数据
- 使用算术和字符串操作来处理数据
- 使用机构化编程概念,比如if-then语句和循环,来为数据处理增加逻辑
- 提取数据文件中的数据元素并将它们按另一顺序或格式重新放置,从而生成格式化报告
gawk options program file
下面显示了gawk程序的可用选项:
2.2) 从命令行读取程序脚本
跟sed编辑器一样,gawk程序会针对数据流中的每行文本执行一遍程序脚本。由于程序脚本被设为显示固定的文本字符串,因而不管你在数据流中输入什么文本,你都会
得到同样的文本输出。
要终止gawk程序,你必须发出信号说明数据流已经结束了。在bash shell中通过组合键ctrl + D来产生EOF字符。
2.3) 使用数据字段变量
gawk的基本特性之一是它处理文本文件中数据的能力。它会自动给每行中的每个数据元素分配一个变量。默认情况下,gawk会将如下变量分配给它在文本行中发现的每个数据字段。
- $0代表整个文本行
- $1代表文本行中的第一个数据字段
- $2代表文本行中的第二个数据字段
- $n代表文本行中的第n个数据字段
2.4) 在程序脚本中使用多个命令
2.5) 从文件中读取程序
2.6) 在数据处理前运行脚本
2.7) 在数据处理后运行脚本
二) sed编辑基础
2.1) 更多的替换选项
你已经懂得了如何用s命令来用新文本替换一行内的文本。但还有一些其他的substitute命令选项能让事情变得更为简单。
2.1.1) 替换标记
要让替换命令对一行中不同地方出现的文本都起作用,必须使用替换标记。替换标记会在替换命令字符串之后设置:
s/pattern/replacement/flags
有4种可用的替换标记:
- 数字,表明新文本将替换第几处模式匹配的地方
- g,表明新文本将会替换所有已有文本出现的地方
- p,表明原来行的内容要打印出来
- w file,表明将替换的结果打印到文件
p替换标记会打印包含与substitute命令中指定的模式匹配的行。这通常会和sed的-n选项一起使用:
2.1.2) 替换字符
有时你会遇到一些文本字符串 中的字符不方便在替换模式中使用。Linux中一个流行的例子是正斜线。替换文件中的路径名会比较麻烦。比如想用C shell替换/etc/passwd文件中的bash shell,你必须这么做:
# sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
上面这样导致很容易出错。要解决这个问题,sed编辑器允许选择其他字符来作为substitute命令中的字符串分割符:
# sed 's!/bin/bash!/bin/csh!' /etc/passwd
2.2) 使用地址
默认情况下,在sed编辑器中使用命令会作用于文本数据的所有行。如果只想要将命令作用于特定某行或某些行,你必须使用行寻址。
在sed编辑器中有两种形式的行寻址:
- 行的数字范围
- 用文本模式来过滤出某行
[address]command
也可以为特定地址将多个命令放在一起:
address {
command1
command2
command3
}
sed编辑器会将指定的每条命令作用到匹配指定地址的行上。
2.2.1) 数字形式的行寻址
当使用数字方式的行寻址时,你可以用它们在文本流中的行位置来引用行。sed编辑器会将文本流中的第一行分配为第一行,然后继续为新行分配行好。
在命令中指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定一定范围内的行。这里有个sed命令作用到指定某行的例子:
指定范围:
如果想将一条命令作用到文本中某行开始到结尾的所有行,你可以用特殊地址-----美元符号:
2.2.2) 使用文本模式过滤器
另一种限制命令作用到哪些行上的方法会稍微复杂一点。sed编辑器允许指定文本模式来过滤出命令要作用的行。格式如下:
/pattern/command
例如:
2.2.3) 组合命令
如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。sed编辑器会处理列在地址行上的每一条命令。
2.3) 删除行
通过指定行数来删除:
通过匹配模式来删除(例如删除第3行):
你可以删除用两个文本模式来删除某个范围内的行。例如:
上面有一个需要注意的地方,就是当匹配到第一个范围时,就会开始进行删除,一直遇到后面的结束范围。如果没有遇到结束范围,则所有的都会被删除掉。例如:
2.4) 插入和附加文本
- 插入(insert)命令i会再指定行前增加一个新行
- 追加(append)命令a会在指定行后增加一个新行
sed '[address]command\
new line'
这里new line中的文本将会出现在sed编辑器输出中你指定的位置。记住,当使用插入命令时,文本会出现在数据流文本的前面:
当使用附加命令时,文本会出现在数据流文本的后面:
2.5)修改行
sed '[address]c\
new line'
2.6) 转换命令
转换(transform,y)命令是唯一可以处理单个字符的sed编辑器命令。转换命令格式如下:
[address]y/inchars/outchars/
例如:
三、gawk进阶
1) 使用变量
所有编程语言共有的一个重要特性是使用变量来存取值。gawk编程语言支持两种不同类型的变量:
- 内建变量
- 自定义变量
1.1) 内建变量
gawk程序使用内建变量来引用程序数据里的一些特殊功能。
1.1.1) 字段和数据行分隔符变量
1.1.2) 数据变量
除了字段和数据行分隔符变量之外,gawk还提供了一些其他的内建变量来帮助你了解数据发生了什么变化并提取shell环境的信息。
ARGC变量表明命令行上有两个参数。这包括gawk命令和data1参数(记住,程序脚本并不算参数)。ARGV数组从代表该命令的索引0开始。第一个数组值是gawk命令
后的第一个命令行参数。
【说明】 跟shell变量不同,在脚本中引用gawk变量时,变量名前不加美元符号
ENVIRON变量看起来可能有点陌生。它使用关联数组来提取shell环境变量。关联数组用文本作为数组的索引值,而不用数值。
数组索引中的文本是shell环境变量,而数组的值则是shell环境变量的值。下面有一个例子:
当你要在gawk程序中记录数据字段和数据行时,FNR、NF和NR变量就能派上用场。有时你不知道数据行中到底有多少个数据字段。NF变量允许你指定数据行中的最后一个数据字段,而不用知道它的具体位置:
NF变量含有数据文件中最后一个数据字段的数字值。你可以在它前面加个美元符将它用作字段变量。
FNR和NR变量彼此类似,但略有不同。FNR变量含有处理过的当前数据文件中的数据行总数,NF变量则含有处理过的所有数据行总数。让我们来看一下几个例子来了解一下这个差别:
在上面的例子中,gawk程序的命令行定义了两个输入文件(它两次指定了同样的输入文件)。这个脚本会打印第一个数据字段的值和FNR变量的当前值。注意,当gawk程序处理第二个数据文件时,FNR值被设回1了。而NR变量则不会重置。
1.2) 自定义变量
1.2.1) 在脚本中给变量赋值
在gawk程序中给变量赋值跟在shell脚本中赋值类似,都用赋值语句:
赋值语句中还可以包含数学算式来处理数字值:
gawk编程语言包含了用来处理数字值得标准数学操作符。其中包括求余符号(%)和幂运算符号(^或**)
1.2.2) 在命令行上给变量赋值:
但是使用命令行参数定义变量值会有个问题。在你设置了变量以后,这个值在代码的BEGIN部分不可用。你可以使用-v命令行参数来解决这个问题:
2) 处理数组
2.1) 定义数组变量
可以用标准的赋值语句来定义数组变量。数组变量赋值的格式如下:
var[index]=element
例如:
2.2) 遍历数组变量
关联数组变量的问题在于你可能无法知晓索引值是什么。跟使用连续数字作为索引值的数字数组不同,关联数组的索引可以是任何东西。
如果要在gawk中遍历一个关联数组,你可以用for语句的一种特殊形式:
for(var in array)
{
statements
}
例如:
注意,索引值不会按任何特定顺序返回,但它们每个都会有个对应的数据元素值。明白这一点很重要,因为你不能指望着返回的值都是按顺序的,你只能确定索引值和数据值是对应的。
2.3) 删除数组变量
从关联数组中删除数组索引要用一个特别的命令:
delete array[index]
删除命令会从数组中删除关联索引值和相关的数据元素值。
例如:
一旦从关联数组中删除了索引值,你就没办法再提取它了。
3) 使用模式
3.1) 正则表达式
3.2) 匹配操作符
匹配操作符允许将正则表达式限定在数据行中的特定数据字段。匹配操作符是波浪线(~)。你要一起指定数据字段变量、匹配操作符(~)以及要匹配的正则表达式:
# $1 ~ /^data2/
上面表示匹配第一个字段为data2开头的数据。
例如:
你也可以用!符号来排除正则表达式的匹配:
$1 !~ /expression/
3.3) 数学表达式
例如:
打印出/etc/passwd中第4列为0的行的第一个字段。
也可以对文本数据使用表达式,但是必须小心。跟正则表达式不同,表达式必须完全匹配。
4) 结构化命令
4.1) if语句
gawk编程语言支持标准的if-then-else格式的if语句。你必须为if语句定义一个评估的条件,并将其用圆括号括起来。如果条件评估为TRUE,紧跟在if语句后的语句会执
行。如果条件评估为FALSE,那这条语句就会被跳过。可以用这种格式:
if(condition)
statements
例如:
4.2) while语句
while语句为gawk程序提供了一个基本的循环功能。下面是while语句的格式:
while(condition)
{
statements
}
例如:
while循环中也支持break与continue语句。
4.3) do-while语句
4.4) for语句
5) 格式化打印
格式化指定字符串采用如下格式:
%[modifier]control-letter
其用法类似于C语言的printf函数的用法:
6) 内建函数
gawk编程语言提供了一些内置函数,可进行一些常见的数学、字符串以及时间函数运算。你可以在gawk程序中利用这些函数来减少脚本中的编码工作。
6.1) 数学函数
6.2) 字符串函数
gawk编程语言还提供了一些可用来处理字符串值得函数。
例如:
上面已经按元素值进行了排序,索引值最小的元素值也最小,而索引值最大的元素值也最大。
6.3) 时间函数
gawk编程语言包含一些函数来帮助处理时间值。
例如:
7) 自定义函数
要定义自己的函数,你必须要用function关键字:
function name([variables])
{
statements
}
去除一个文件中所有重复的数字: