Linux Shell 高级编程(下)

1.概述

在前面的章节中,我们已经介绍了shell结构化命令,函数用法,正则表达式以及sed,gawk的基本用法,在这里,主要介绍一下sed和awk的高级用法。sed是流编辑器,具有速度快的优点。sed 每次处理一行数据,在每一行数据上执行脚本命令。而gawk是一种编程语言,能够对数据进行处理,从而生成数据报告。

2. sed的高级用法

(1)n与N的区别

n: 小写n告诉sed编辑器移动到数据流中文本的下一行,而不是回到命令的开始。

N: 大写N是将数据流的下一行添加到模式空间中去。即将数据流的两个文本行合并,添加到同一模式空间中去。虽然文本行以换行符分隔,但此时流sed编辑器已经将他们作为同一个文本进行处理了。

例子:

[root@localhost chapter18]# cat <data1this is the header linethis is a data line.this is the last line.

data1中的数据隔一行有一个空行。

小写n:

[root@localhost chapter18]# sed ‘/header/{nd}’ data1this is the header linethis is a data line.this is the last line.

{}表示在匹配header上执行下面的脚本。

即匹配了data1中的第一行,然后n,移动到下一行,d删除下一行文本,此时就删除了空行。

大写N:

[root@localhost chapter18]# sed ‘/header/{Nd}’ data1this is a data line.this is the last line.

匹配了data1中的第一行,N将下一行添加到模式空间中去,那么此时流编辑器就把它们作为一行处理了,虽然它们之间有换行符,所以d就删除了两行,分别是第一行与空行。

有{}与无{}的区别:

[root@localhost chapter18]# sed ‘/header/n;d;’ data1this is the header line

这个命令与上面的命令形式是一样的,只不过用;将命令分隔开了。

首先匹配header,然后下一行,之后一直删除下一行。所以只留下了第一行。

[root@localhost chapter18]# sed ‘/header/N;d;’ data1

如果是N就全删除了。

首先匹配header,然后把下一行添加到模式空间中去,d删除掉模式空间的内容。之后扫描下一行,N把next添加到模式空间中去……

(2) D与d的区别

d是删除模式空间的当前行。

而D只删除模式空间的第一行,直至换行符的所有字符,也包括换行符。

d Delete pattern space. Start next cycle.、

D Delete up to the first embedded newline in the pattern space. Start next cycle, but skip reading from the input if there is still data in the pattern space.

区别一下:

d 删除模式空间,进行下一个循环,即处理下一行。

D删除模式空间的第一行,如果此时模式空间仍然有数据,强制返回到脚本的开头,则不处理新的输入的数据。

例子1:

[root@localhost chapter18]# cat <data5This is the header line.this is the second line.this is the last line.

[root@localhost chapter18]# sed ‘/^$/{> N> /heade/D}’ data5This is the header line.this is the second line.this is the last line.

例子2:

[root@localhost chapter18]# sed ‘/^$/N;/header/D’ data5this is the second line.this is the last line.

这是个比较经典的例子。

分析:

目标: 删除第一行之前的空行。

例子1:

首先通过^$找到空行,然后N将下一行添加到模式空间中去,/header/D 匹配模式空间的内容,所以D就删除了第一个行,直到换行符为止。

而此时模式空间还有内容,但由于不是空行,与/^$/不匹配所以就不会执行下面的脚本。

所以最终只删除了模式空间的第一行。

例子2:

前面的过程是一样的,通过D删除了第一个行,直到换行符为止。但由于模式空间中还有内容,必须处理, 与^$不匹配,不执行N,但与/header/D匹配,所以就删除了第二行。所以header那一行也被删除了。

主要是{}里面的命令都会执行到前面的模式匹配上。

对于D,d,n,N,{},一定要注意区分。

(3)保留空间与模式空间

模式空间是一个活动缓冲,它在sed编辑器处理命令时保留被检查的文本。然而,它并不是文本唯一可存储的可用空间。

而保留空间是一个缓冲区,在处理模式空间的内容时,可以用保留空间暂时保留文本行。

sed 保留空间的命令

h 将模式空间的内容复制到保留空间

H 将模式空间的内容追加到保留空间

g 将保留空间的内容复制到模式空间

G 将保留空间的内容追加到模式空间

x 将模式空间的内容与保留空间的内容互换

[root@localhost chapter18]# cat <data2this is the header line.this is the first data line.this is the second data line.this is the last line.

[root@localhost chapter18]# sed -n ‘/first/{> h> p> n> p> g> p> }’ data2this is the first data line.this is the second data line.this is the first data line.

分析:

sed -n ‘/first/ 找到first那行。

h 将first那行复制到保留空间中去。

p 打印模式空间的那一行,即first那一行。

n 移动到下一行即second那一行。

p 打印模式空间的行即second.

g 将保留空间的复制到模式空间。

p 即打印first那一行。

[root@localhost chapter18]# sed -n ‘/first/{hnpgp}’ data2this is the second data line.this is the first data line.

先打印了second,再打印了first.

(4)反转整个文本

!表示否定命令。 !p表示不激活命令。

[root@localhost chapter18]# sed -n ‘/header/!p’ data2this is the first data line.this is the second data line.this is the last line.

即找到header时不打印。

反转整个文本:

[root@localhost chapter18]# cat <data2this is the header line.this is the first data line.this is the second data line.this is the last line

[root@localhost chapter18]# sed -n ‘1!G;h;$p’ data2this is the last line.this is the second data line.this is the first data line.this is the header line.

原理:

(1)将一行放到保留空间中

(2)将文本的下一行放到模式空间中去

(3)保留空间追加到模式空间中去

(4)将模式空间放到保留空间中去

(5)重复第2到4,直到所有的以相反的顺序放到保留空间

(6)检索并打印

$p表示最后一行才打印。

(5)分支

分支命令允许在数据流的特定子集上执行命令。

[address] b [label]

address决定由哪一行或者是哪些行激发命令。

label决定于何处分支。如果label参数不存在,则分支命令分支到脚本结尾。

[root@localhost chapter18]# sed ‘2,3b;s/this is/is this/’ data2is this the header line.this is the first data line.this is the second data line.is this the last line.

在2,3行进行分支,由于没有label,则分支到了脚本的结尾。即跳过了2,3执行替换命令。

[root@localhost chapter18]# sed ‘/first/b jump1;s/ is/might be/;s/line/test/;:jump1 s/data/text/’ data2thismight be the header test.this is the first text line.thismight be the second text test.thismight be the last test.

如果匹配first就跳到jump1处执行。如果不匹配就执行3个替换命令,包括jump1后的替换命令。

[root@localhost chapter18]# echo "this,is,a,test,to,remove,commas."|sed -n ‘:start s/,/ /p;/,/b start’this is,a,test,to,remove,commas.this is a,test,to,remove,commas.this is a test,to,remove,commas.this is a test to,remove,commas.this is a test to remove,commas.this is a test to remove commas.

:start 跳到start.

s/,/ /将,替换成空格。

/,/ b start如果有逗号就替换。

即删除文本中的逗号。

(6)测试

[address]t [label]

如果替换命令成功匹配了并替换了一个模式,那么测试命令分支到指定的标签。

如:

[root@localhost chapter18]# cat <data2this is the header line.this is the first data line.this is the second data line.this is the last line.

[root@localhost chapter18]# sed ‘s/first/starting/;t;s/line/test/’ data2this is the header test.this is the starting data line.this is the second data test.this is the last test.

找到first并且成功替换,则脚本跳过后续命令。

即第2行的line没有被替换。

接着脚本读下一行进行命令处理。所以第三行的被替换了。

[root@localhost chapter18]# echo "this,is,a,test,to,remove,commas."|sed -n ‘:start s/,/ /p;t start’this is,a,test,to,remove,commas.this is a,test,to,remove,commas.this is a test,to,remove,commas.this is a test to,remove,commas.this is a test to remove,commas.this is a test to remove commas.

如果成功替换了,则继续执行start.

(7)模式替换

与号(&)是用来替换命令中的匹配模式,无论什么样的匹配模式在命令中都可以用与号来替换它。

[root@localhost chapter18]# echo "the cat sleeps in his hat"|sed -n ‘s/.at/"&"/p’the "cat" sleeps in his hat

即把cat加双引号。由于没有g所以后面的hat没匹配上。

sed使用圆括号定义替换模式中的子字符串元素。然后用特定的符号来引用了字符串元素,数字表示子字符串的位置,/1表示第一个圆括号,/2表示第二个圆括号。

[root@localhost chapter18]# echo "1234567"|sed ‘:start s//(.*[0-9]/)/([0-9]/{3/}/)//1,/2/p; t start’1234,5671,234,5671,234,567

分析:

t测试前面的替换命令。

.*表示任意个字符。[0-9]以数字结尾。

[0-9]{3}以3数字结尾。

/1表示第一个括号。

/2表示第二个括号。

即把第一个括号与第二个括号中间加上一个逗号。

(8)行距

a.双倍行距

[root@localhost chapter18]# sed ‘$!G’ data2this is the header line.this is the first data line.this is the second data line.this is the last line.

G是将保留空间的内容追加到模式空间,保留空间的默认值是一个空行。$!G表示最抂行不添加 。

b. 对有可能空行的文件使用双倍行距

先删除所有的空行,再使用双倍行距。

[root@localhost chapter18]# cat <data6This is line one.This is line two.This is line three.This is line four.

[root@localhost chapter18]# sed ‘/^$/d;$!G’ data6This is line one.This is line two.This is line three.This is line four.

(9)对文件中的行计数

=打印行号,N将下一行添加到模式空间,再用空格替换/n即可。

[root@localhost chapter18]# sed ‘=’ data2|sed ‘N;s//n/ /’1 this is the header line.2 this is the first data line.3 this is the second data line.4 this is the last line.

(10)打印文件最后几行

打印文件的最后几行可以造成滚动的效果。

[root@localhost chapter18]# sed ‘:start $q;N;5,$D;b start’ /etc/passwdsamba1:x:503:503::/home/samba1:/bin/bashtestuser:x:507:507::/home/testuser:/bin/bashroot2:x:508:508::/home/root2:/bin/bashchenjinzhong:x:509:509::/home/chenjinzhong:/bin/bash

:start一直循环,如果是最后一行($),则退出(q).

如果是第5行,D删除模式空间的第一行。

这样会显示最后4行。

(11)删除行

a.删除连续的空行

当遇到一个非空行与一个空行则不删除。

[root@localhost chapter18]# sed ‘/./,/^$/!d’ data6This is line one.This is line two.This is line three.This is line four

b. 删除开头的空行

即任意字符到结尾都不会被删除。

[root@localhost chapter18]# cat <data7this is the first linethis is the second line.[root@localhost chapter18]# sed ‘/./,$!d’ data7this is the first linethis is the second line.

c.删除结尾的空行

[root@localhost chapter18]# cat <data8this is the first linethis is the second line[root@localhost chapter18]# sed ‘:start /^/n*$/ {$d;N;b start}’ data8this is the first linethis is the second line

分析: ^/n*$ 即可能包括一个或多个空行。 /n* 0个或多个。 ^$一个空行。所以整个就是一个或多个空行。

如果是最后一行则删除$d,如果不是最后一行,则将下一行添加到模式空间中去。

3. gawk的高级用法

(1)变量

a. gawk字段分隔符与记录分隔符。

FIELDWIDTHS 以空格分割的数字列表,用空格定义每个数据字段的精确宽度

FS 输入字段分割符

RS 输入记录分割符

OFS 输出字段分割符

ORS 输出记录分割符

[root@localhost chapter19]# cat data1data11,data12,data13,data14,data15data21,data22,data23,data24,data25data31,data32,data33,data34,data35[root@localhost chapter19]# gawk ‘BEGIN {FS=",";OFS="-"} { print $1,$2,$3}’ data1data11-data12-data13data21-data22-data23data31-data32-data33

FS指定输入字段分割符,而OFS指定输出字段分割符。

RS记录输入分割符。

[root@localhost chapter19]# cat <data2Riley Mullen123 Main StreetChicago,1L 60601(312)555-1234Frank Williams456 Oak StreetIndianapolis, IN 46201(317)555-9876Haley Snell4231 ELM streetDetroit,MI 48201(313)555-4938[root@localhost chapter19]# gawk ‘BEGIN{FS="/n";RS=""}{printf "%s%s/n",$1,$4}’ data2Riley Mullen(312)555-1234Frank Williams(317)555-9876Haley Snell(313)555-4938

b.关联数组

关联数组使用文本而不是使用数值作为数组的索引。

[root@localhost chapter19]# gawk ‘BEGIN{print ENVIRON["PATH"];print ENVIRON["HOME"]}’/usr/lib/qt-3.3/bin:/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/opt/real/RealPlayer:/root/bin:/opt/real/RealPlayer:/opt/real/RealPlayer:/opt/real/RealPlayer:/opt/real/RealPlayer/root

ENVIRON变量使用关联数组来检查shell变量 。

[root@localhost chapter19]# gawk ‘BEGIN{FS=":";OFS=":"}{print $1,$NF}’ /etc/passwdroot:/bin/bashbin:/sbin/nologindaemon:/sbin/nologinadm:/sbin/nologinlp:/sbin/nologinsync:/bin/syncshutdown:/sbin/shutdownhalt:/sbin/haltmail:/sbin/nologin

FS输入字段分割符为:,输出字段分割符也为:

NF表示最后一个字段的数组,那么$NF就表示最后一个字段的值。

c.用户自定义变量

[root@localhost chapter19]# gawk ‘BEGIN{testing="this is a test";print testing;}’this is a test

定义一个testing变量,然后print输出。

[root@localhost chapter19]# cat <script1BEGIN{FS=","}{print $n}[root@localhost chapter19]# gawk -f script1 n=2 data1data12data22data32

-f执行脚本。设置变量n的值。但有一个问题,在命令行中设置的n的值在代码的BEGIN部分不能使用。可以通过-v来解决。

[root@localhost chapter19]# cat <script2BEGIN{ print "the starting value is",n;FS=","}{ print $n }[root@localhost chapter19]# gawk -f script2 n=3 data1the starting value is data13data23data33

[root@localhost chapter19]# gawk -f script2 -v n=3 data1the starting value is 3data13data23data33

这样在BEGIN部分就能使用了。

(2)数组

定义数组变量:

var[index]=element

#!/bin/bashgawk ‘BEGIN{var[0]=1var[2]=3total=var[0]+var[2]print total}’

(3)使用模式

匹配操作符: ~

#!/bin/bashgawk ‘BEGIN{FS=":"}{if ($1 ~/root/)print $1,$NF}’ /etc/passwd

$1 ~/root/ 表示第一个字段匹配root.

(4)结构化命令

if语句:

[root@localhost chapter19]# cat <data4105135034

#!/bin/bashgawk ‘{if($1>20){x=$1*2print x}}’ data4

while语句:

[root@localhost chapter19]# cat <data5100 110 120130 140 150160 170 180

#!/bin/bashgawk ‘{total=0i=1while(i<4){total+=$ii++}avg=total/3print "Average:",avg}’ data5

结果:

[root@localhost chapter19]# ./script8Average: 110Average: 140Average: 170

当while计算出第一个记录的值时,会去读下一条记录,然后去计算,记录是以换行符为分隔符的。

(5)格式化打印

printf "format string",var1,var2…..

#!/bin/bashgawk ‘BEGIN{FS="/n"RS=""}{printf "%s-%s/n",$1,$4}’ data2

结果是:

[root@localhost chapter19]# ./script10Riley Mullen-(312)555-1234Frank Williams-(317)555-9876Haley Snell-(313)555-4938

关于awk和sed的高级编程就介绍到这里了。

接受失败等于打破完美的面具,接受失败等于放松自己高压的心理,

Linux Shell 高级编程(下)

相关文章:

你感兴趣的文章:

标签云: