Linux 编辑器(四)--gawk与sed

来源:互联网 发布:张居正 高拱 知乎 编辑:程序博客网 时间:2024/06/07 22:34

awk是模式扫描和处理语言,又称过滤器,是Linux/Unix下用来操纵数据和产生报告的程序语言。其处理的数据可以来自标准输入、一个或多个文件、或者其他命令的输出。

awk写于1977年,其名称取自其作者姓氏的第一个字母,分别是Alfred V Aho, Peter J. Weinberger和Brian W Kernighan。在1985年的升级版中加入了用户自定义函数、动态正则表达式和处理输入文件等功能,使得awk可以处理大型程序。awk有多个版本,包括旧版awk、nawk(new awk,新版本)、gawk(GNU版本)以及POSIX awk等。很多Linux系统将/bin/awk链接至/bin/gawk。

gawk程序由包含模式和动作的单行或多行文本组成,格式如下:
pattern {action}
模式用来从输入中选取文本行,对于匹配的每行文本,gawk都执行fcwt(action)来处理。

模式用斜杠包装,如:gawk /word/ filename查找所有包含word的行。这里没有指定action,默认为print。

“~”用于测试一行中某段是否匹配模式。一行中可以有多个“段”,各“段”之间由空白符号分隔,如section1 secion2 section3。gwak '$1~/word/' filename在文件中查找第一段匹配word的各行。“!~”用于测试不匹配,如gawk '$2!~/word/' filename。

BEGIN和END是两个独特的模式,分别是在gawk开始处理之前和处理完毕之后要执行的命令。在处理所有输出之前,gawk执行BEGIN模式关联的动作,在处理完之后执行END模式关联的动作。例:gawk 'BEGIN{print "This is the beginning"} {print}' filename。又例: gawk 'BEGIN {print "head"; count=0} {print; count++} END {printf "end: %d", count}' filename。

逗号“,”可以分隔两个模式,作为范围运算符。例:gawk '/abcd/,/efgh/' filename 输出第一行匹配abcd而最后一行匹配efgh的多行。

{print}是默认的动作,不带参数时则输出记录(通常是一行)。参数可以是变量或字符串常量。还可以指定输出到文件,如>(覆盖)、>>(追加)、|(管道)和|&(协进程,双向管道)。例:{print > "somefile"}。多个动作用分号“;”隔开。

“#”开头的程序行表示注释。

gawk不需要声明可以直接使用变量。未赋初始值的变量自动初始化为0或空字符串。除了用户变量(user variable)外,gawk还维护了程序变量(program variable):
$0:当前记录(作为单个变量);
$1-$n:当前记录的字段;
FILENAME:当前输入文件名(null表示标准输入);
FS:输入字段的分隔符;
NF:当前记录的字段数目;
NR:当前记录的记录编号,每处理完一个记录,NR的值就自动加1;
OFS:输出字段分隔符(默认为空格符);
ORS:输出记录分隔符(默认为换行符);
RS:输入记录分隔符(默认为换行符);

除了在程序中初始化变量外,还可以在命令行上用“--assign”或“-v”选项初始化变量。如:awk -v A=1 '{print A}' filename。

gawk把输入文件看作是一个具有一定格式的记录集合,通常一行为一个记录,每个记录被空格分隔成多个字段。-F选项可以指定字段分隔符,如:awk -F: '{print $2}' filename指定分隔符为冒号“:”。而awk -F[:-] ‘{print $2}' filename指定分隔符为冒号“:”或者连字符“-”,即记录1:2-3被视为1、2、3三个字段。输入段分隔符也可以在模式中指定:awk 'BEGIN{FS="[:-]"} {print $2}' filename。

{print $2 $3}会把两个变量合并输出,例如,$2为"a",$3为"b",则前述动作输出“ab”,{print $2,$3}输出"a b"。输出段分隔符可以在BEGIN模式中指定:awk BEGIN{OFS=":"} {print $2}' filename。

gawk支持函数,自带的函数有:
length(str):返回str的字符个数。不带参数则返回当前记录的字符个数。
int(num):返回num的整数部分。
index(str1,str2):str2在str1中的位置,如果不在则返回0。
split(str,arr,del):用del作定界符,将str的元素放置到数组arr[1]...arr[n]中,返回数组元素个数。
sprintf(fmt,args):根据fmt格式化args并返回格式化后的字符串,模仿C编程语言中相应的同名函数。
substr(str,pos,len):返回str中从pos开始长度为len个字符的字符串。
tolower(str):返回str的副本,所有大写字母被转换成小写。
toupper(str):返回str的副本,所有小写字母被转换成大写。

gawk支持关联数组,即array[string]=value。关联数组可以用for来遍历。例:awk '{array[$1]=$2} END{for (elem in array) print elem}' filename。

printf用于格式化输出,语法为:%[-][x[.y]]cov。“-”表示左对齐,“x”表示最小字段宽度,“y”表示数字中小数点右边的位数。“conv”指示数值转换的类型,包括:
d:十进制;
e:指数表示;
f:浮点数字;
g:使用f或e中较短的那个;
o:无符号八进制;
s:字符串;
x:无符号十六进制。

gawk支持关系运算符,包括<、>、<=、>=、==、!=。布尔结果可以用布尔运算符“&&”和“||”来运算。算术运算符支持“+”、“-”、“*”、“/”、“=”、“%”、“++”、“--”、“+=”、“-=”、“*=”、“/=”和“%=”。与C基本一样。

流程控制有if...else,while,for,break和continue。

getline可以用来读取文件中的一行。如awk 'BEGIN {getline myline; print myline}' myfile只输出文件的第一行,可以用循环来控制读取的行数。

awk程序可以写在一个文件里作为脚本文件,再用-f选项指定。例:awk -f myscript filename。

协进程(coprocess)是指与另一个进程并行运行的进程。gawk从版本3.1开始可以通过协进程方式直接与某个后台进程进行信息交换。协进程可用于客户端/服务器环境中(例如构建SQL前端与后端),或者通过网络与远程系统交换数据。例如,下面这个awk脚本:
BEGIN {
    count = 0;
    while (getline myline) {
        print myline |& "./coprocess";
        "./coprocess" |& getline LINE;
        print LINE;
    }
}
它将与同一目录一下的可执行文件coprocess进行协作。下面是coprocess文件的内容:
#!/bin/bash
while read LINE
do
    echo "tommy" $LINE
done
之后运行命令:gawk -f myscript filename,则在filename中的每行前加上“tommy”再输出到标准输出。

在协进程的基础上,gawk还可以通过网络交换数据。gawk可以通过IP网络连接到另一个系统上的某个进程进行信息交换。当用户使用“/inet/”开头的特殊文件名时,gawk将使用网络连接来处理用户的请求。以“inet”开头的特殊文件的格式如下:
/inet/protocol/local-port/remote-host/remote-port
其中“protocol”通常是TCP,也可以是UDP。“local-port”表示本地端口,0表示让gawk自动选择一个端口。“remote- host”表示远端主机的地址,“remote-port”表示远端端口号,可以指定为http或ftp地址。比如下面的代码:
BEGIN {
    server = "/inet/tcp/0/www.google.com/80";
    print "start to connect..";
    print "GET /index.html\r\n" |& server;
    print "request sent."
    while (server |& getline)
        print $0;
    print "done."
}


sed(stream editor,流编辑器)是一个批处理(非交互式)编辑器,可以实现在vi和ex里一样的编辑任务。sed相当小巧,可以利用管道对标准输入/输出的数据进行编辑和组合。sed可以对来自文件或标准输入流进行转换,通常被用作管道中的过滤器。由于sed仅仅对其输入进行一遍扫描,因此比其他交互编辑器(如 ed)更加高效。

sed一次处理一行文本并把处理结果输出送往标准输出设备(屏幕)。sed把当前处理的行存储在模式空间(pattern space)的临时缓冲区中。一旦sed完成对模式空间中行的处理(也就是执行完这一行中的sed命令),模式空间中的行就被送往屏幕(除非命令是删除行或者打印到打印机)。行被处理完成以后,就被移出模式空间,程序接着读入下一行,处理、显示、移出……文件输入的最后一行被处理完成以后,sed结束。通过在临时缓冲区存储每一行,然后在缓冲区中操作该行,保证了原始文件不会被破坏。

sed命令行的语法格式为:
sed [-n] program [file-list]
sed [-n] -f program-file [file-list]
其中“program”表示命令行中的sed程序,“program-file”表示一个sed程序文件。“file-list”表示sed将要处理的普通数据文件。“-n”选项阻止sed将选定的行复制到标准输出上。

sed程序由符合如下语法的一行或多行命令构成:
[address][, address] instruction [argument-list]

其中“address”是可选的,如果省略则对输入的所有行进行处理。地址可以以行号、正则表达式或二者结合的方式表示。比如sed -n '3,/abcd/ p' filename输出第3行到匹配“abcd”的那行。其中“p”是打印指令。“$”表示最后一行。

指令包括:
d:删除指令(delete)。导致sed不输出被选择的行,并且不继续完成对该行的后续处理过程。
n:下一条指令(next)。输出当前选择的行(如果可以),然后从输入中读入下一行,并且从program或program-file中的下一条指令开始对新读入的行进行处理。
a:追加指令(append)。在当前选择的行之后插入一行或多行文本。如果在a指令前有两个地址,那么它将在选定的每一行之后添加文本。如果a指令前没有地址,它将在每行之后添加文本。
i:插入指令(insert)。与a指令同,只不过是将文本添加到选定的行之前。
c:修改指令(change)。与a和i指令类似,不同的是它将选定的行修改为新的文本。如果指定的是一个地址范围,c指令将整个范围内的行替换成新文本。
s:替换指令(subsitute)。与vim中的替换指令类似。语法为s/pattern/replacement/[g][p][w file]。g为global,替换所有匹配;p为print,打印替换过的行;w为写入文件。
p:打印指令(print)。将选定的行写入标准输出。
w file:写指令(write)。把选定的行写入文件。
r file:读指令(read)。从文件中读出内容并添加到选定的行之后。
q:退出指令(quit)。立即结束sed。

sed具有一此控制结构,可以控制程序的流程走向。常用的控制结构有:
!:取反操作,使得sed后面与其同一行的指令作用于没有该指令选择的每一行地址上。例如,“3!d”将删除除第3行外的所有行,而“$!p”将显示除最后一行外的所有行。
{}:将一组指令括起来,用于定义指令组。该组指令将作用于它前面的地址所选定的行,使用分号可将一行中的多条指令分隔开。
分支指令:sed不适合使用分支指令,如果需要,建议使用awk或Perl来编写程序。
:label:标识sed程序中的一个位置,适用于分支指令b或t
b [label]:无条件转移到label标识的命令。如果没有指出分支目标label,则跳过当前行中剩下的指令并从输入中读取下一行。
t [label]:如果从输入中读取的行使得s指令匹配成功,则转移到label标识的命令。如果没有指出分支目标label,则跳过当前行中剩下的指令并从输入中读取下一行。

sed有两个缓冲区:Pattern区和Hold区。Pattern区中保存着sed从输入中读取的行,所有的命令都工作在Pattern区上。Hold区作为临时缓冲区,可以用来暂存数据。Pattern区和Hold区之间进行数据传送的指令有:
g:将Hold区中的内容复制到Pattern区中。Pattern区中原来的内容将会丢失。
G:将一个换行符和Hold区中的内容追加到Pattern区中的内容之后。
h:将Pattern区中的内容复制到Hold区中。Hold区中原来的内容将会丢失。
H:将一个换行符和Pattern区的内容附加到Hold区中的内容之后。
x:交换Pattern区和Hold区中的内容。

原创粉丝点击