grep gawk

11.2.1grep和正则表达式

让我们首先从grep命令开始。这个命令大家应该很熟悉了,它用来在文件中查找一个
字符串。不过,实际上,grep的处理功能要强大和复杂的多。

grep命令的语法是

grep[模式][文件名]

如果没有给出文件名,就缺省使用标准输入。grep每次读取一行,并且和给出的模式
进行匹配,如果成功就把这一行会显,例如:(粗体的是我们输入的内容)

$greptest

close

testmyhand

testmyhand


grep的“模式”也称为正则表达式,可以由各种基本的正则表达式元素构成。正则表
达式元素主要包括下面几种:

字符串匹配任何字符串,例如greptest表示在标准输入中1

[...]封闭集中匹配一个字符,如:[abcde]可以匹配a,b,c,d,e

[^...]求补集中匹配一个字符,例如[^ABC]匹配

.匹配任意字符

/s空白符

/S非空白符

/d数字

/D非数字

/w字母或数字

/W非字母和数字

*匹配任何字符

上面的形式是grep中使用的基本正则表达式,另外,还可以使用egrep,egrep是grep
的一个扩展版本,支持下面这些扩展的正则字符串:

^匹配一行的开始

$匹配一行的结尾

()确定正则表达式求值顺序,和正常演算中的括号意思差不多。

(...|...|...)或,可选项之一进行匹配,例如:(abc|dev|ghi)可以匹配abc,dev,gh
i,而(ww|gg)do可以匹配wwdo或者ggdo。

+一次或多次模式

如:aba+匹配aba,abaa...不匹配ab

通常,我们有两种方法使用grep和egrep,一种是使用管道,例如我们应该熟悉的ps
ax|grepsendmail,另一种是直接在文件中搜索对应的字符串。

grep/egrep还可以在命令行使用开关,常用的开关包括:

-b在行前加上块号

-c统计匹配行的个数

-n在行前加上行号

-w将模式解释为字符串,所有正则表达式的控制命令失效

-x精确匹配

-r查询文件时包含子目录

举个例子来说,我们想在/var/log/httpd/access_log中查询所有不是来自本地(192
.168.0.1)的请求记录,可以执行:

grep–v"^192.168.0.1"/var/log/httpd/access_log

^用来让grep只在行首匹配。

在grep查询的时候可以使用通配符代表多个文件,例如,grepstart*-r将在当前目
录以及所有子目录的所有文件中查询start字符串。

11.2.2gawk的使用方法

gawk是awk的一个实现,awk是一种用来处理报告等文本文件的脚本语言。不过,我们
介绍这个产品的主要目标是用它来处理各种程序的记账文件。对于复杂的脚本,还是用
Perl比较合适。

gawk的主要功能是针对档案的每一行搜寻指定的模式。,每当找到一个匹配的模式
,gawk就会去执行你设定的动作。按照这个方式,gawk依此方式处理输入档案的每一
行直到输入档案结束。如果对于某个模式没有设置对应的动作,gawk将直接将这个行显
示出来。

为了使用gawk,你通常必须先写一个awk脚本,除非模式/动作非常简单,可以在一行
上完成。我们用一个例子来解释gawk的基本用法,首先产生一个目录列表文件:

ls–l/etc>list

现在list的内容有点像这样:

total2164

drwxr-xr-x3rootroot4096Feb1522:55CORBA

-rw-r--r--1rootroot2045Sep241999DIR_COLORS

-rw-r--r--1rootroot17Mar2519:59HOSTNAME

…………

现在我们选择一个最简单的例子,简单地查找所有属性是drwxr-xr-x的目录文件:

gawk'/drwxr-xr-x/{print$0}'list

将输出所有这样的目录。

这个例子看上去没有什么实际用处,因为用grep也可以做同样的动作,那么我们可以
看一看下面这个功能:

$gawk'$1=="-rwxr-xr-x"{sum=sum+$5}END{printsum}'list

15041

这个是什么意思?对于所有属性是755的文件,让gawk对第五栏的数字求和。第五栏我
们可以看到就是文件的长度,因此这个命令将显示所有属性为755的文件的总共的长度。

$n是gawk中非常重要的概念,它用来表示文本串的分栏。缺省的情况下,gawk将输入
字符串(从文件中读入的每一行)按照分割的空格分成若干个字段,每个字段作为一个
变量,例如有一行

mynameis3thtest

那么,在awk读入这一行之后,就产生了$1到$5变量,其中$1="my",$2="is",………
,最后$5="test"。另外还有一个特殊的变量$0,它表示整个输入行,也就是这个字符串
"mynameistest"。另外还有一个特殊的变量NF,它表示当前行的字段的个数,在现在
的情况下,NF应该等于5。

在某些特殊的情况下,你可能需要改变分割符的定义,这可以通过对FS赋值来完成,
例如FS=","将分割符定义为都号而不是缺省的空格。

在一般情况下,gawk可以从命令文件中获得模式/动作,命令文件的格式很简单,就是
直接将应该写在命令行上的模式/动作对写在文件里面,每个对构成一行,模式可以有两
种,一种是模式匹配,也就是我们在前面解释的正则表达式,如果使用正则表达式,那
么需要用两个/把它们夹在一起,例如/[A-Z]/表示正则表达式[A-Z]。

另一种模式是比较指令,比较指令可以用比较操作符和逻辑运算符来构成,常用的比
较操作符有:

==等于<=不大于~按照正则表达式匹配

<小于>=不小于!~按照正则表达式不匹配

>大于!=不等于

逻辑运算符有

&&和||或!非()括号


设定了模式后,就可以设置对应的动作了,在gawk中,动作必须用花括号括起来。ga
wk能完成的动作并不多,毕竟它是一种报告分析语言。一般情况下,只要熟悉print和p
rintf命令就足够了,print命令的格式非常简单:

printitem1,item2,…………

输出时,每个项目输出一栏,中间用空格分开。一个print后面不跟着任何变量会导致
gawk显示当前的输入行($0)。如果要输出一个字符串,使用引号把它括起来,特别是
如果要输出一个空行,使用print""。这里是一个例子,它将list文件的头两栏输出:

gawk'{print$1,$2}'list

由于输入的文本文件内容有多行,你在命令栏中设计的模式/动作会对每一行执行一次
。就是:

total2164

drwxr-xr-x3

-rw-r--r--1

-rw-r--r--1

-rw-r--r-1

…………………

如果你要精确地控制输出,也可以使用printf命令,这个命令的格式是:

printfformat,item1,item2,...

format参数就是C语言里面的格式控制符,例如%c,%d,%f等等。在%与格式控制
字母之间可加入modifier,modifier是用来进一步控制输出的格式。可能的modifie
r如下所示:

'-'使用在width之前,指明是向左靠齐。如果'-'没有出现,则会在被指定的
宽度向右靠齐。例如:

printf"%-4S","foo"会印出'foo'。

'width'这一个数字指示相对应的栏位印出时的宽度。例如:

printf"%4s","foo"会印出'foo'。

width的值是一个最小宽度而非最大宽度。如果一个item的值需要的宽度
比width大,则不受width的影响。例如printf"%4s","foobar"将印出'foobar'。

'.prec'此数字指定印出时的精确度。它指定小数点右边的位数。如果是要印出一个
字串,它指定此字串最多会被印出多少个字符。

作为一种脚本语言,gawk允许使用变量,定义变量非常简单,就是直接用等号对它赋
值。为了在gawk程序的开始处对变量赋值,gawk专门提供了BEGIN语句,这个语句将在所
有行被读入之前执行,而且只执行一次,通常用它来执行初始化命令,例如

BEGIN{sum=0;count=0;average=0.0;}

对于变量可以使用数学表达式进行运算,运算符包括常见的加减乘除算符,以及^(乘
方),%(取余)和著名的++,--。不过注意gawk在做除法的时候总是使用浮点除法,除了
取余算符%。

函数

另外,gawk包含下列函数:

数学函数

atan2(x,y)y/x的正切

cos(x)余弦函数

sin(x)正弦函数

int(x)取整

log(x)取自然对数

exp(x)指数函数

rand(x)生成一个0到1之间的随机数

srand()初始化随机数发生器

systime()返回从1970年1月1日0:00到当前时间的秒数

sqrt(x)取x的平方根

字符串函数

index(string1,string2)

它会在string1里面,寻找string2第一次出现的地方,返回值是字串string2出
现在字串string1里面的位置。如果找不到,返回值为0。

例如:

printindex("peanut","an")

会印出3。

length(string)

string字符串的长度

例如:

length("abcde")

是5。

match(string,regexp)

match函数会在字串string里面,寻找符合regexp的最长、最靠左边的子字
串。返回值是regexp在string的开始位置,即index值。这个函数会设定内部变量
RSTART等於index,内部变量RLENGTH等於符合的子串个数。如果不符合,则会设定
RSTART为0、RLENGTH为-1。

sprintf(format,expression1,...)

跟C语言的sprintf差不多。

例如:

sprintf("pi=%.2f(approx.)',22/7)

传回的字串为"pi=3.14(approx.)"


sub(regexp,replacement,target)

在字串target里面,寻找符合regexp的最长、最靠左边的地方,并且以字串
replacement代替最左边的regexp。

例如:

str="water,water,everywhere"

sub(/at/,"ith",str)

结果字串str会变成

"wither,water,everywhere"


gsub(regexp,replacement,target)

gsub与前面的sub类似。在字串target里面,寻找符合regexp的所有地方
,以字串replacement代替所有的regexp。

例如:

str="water,water,everywhere"

gsub(/at/,"ith",str)

结果字串str会变成

'wither,wither,everywhere"

substr(string,start,length)

传回字串string的子字串,这个子字串的长度为length个字符,从第start
个位置开始。

例如:

substr("washington",5,3)

传回值为"ing"

如果length没有出现,则传回的子字串是从第start个位置开始至结束。

例如:

substr("washington",5)

传回值为"ington"

tolower(string)

将字串string的大写字母改为小写字母。

例如:

tolower("MiXeDcAsE123")

传回值为"mixedcase123"


toupper(string)

将字串string的小写字母改为大写字母。

例如:

toupper("MiXeDcAsE123")

传回值为"MIXEDCASE123"

其他函数

system(command)

此函式允许使用者执行作业系统的指令,执行完毕後将回到gawk

程式。

例如:

BEGIN{system("ls")}

控制流

在gawk命令脚本中可以使用控制流,主要是if,for,while等语句,用法和C语言相当
类似:


if(condition)then-body[elseelse-body]

如果condition为真(true),则执行then-body,否则执行else-body。

举一个例子如下:

if(x%2==0)

print"xiseven"

else

print"xisodd"


while(condition)

body

while语句测试condition表达式。假如condition为真则执行body的语句。一次
执行完後,会再测试condition,假如condition为真,则body会再度被执行。这个
过程会一直被重复直到condition不再是真。如果condition第一次测试就是伪(fals
e),则body从没有被执行。

下面的例子会印出每个输入行的前三个栏位。

gawk'{i=1

while(i<=3){

print$i

i++

}

}'

do

body

while(condition)

这个doloop执行body一次,然後只要condition是真则会重复执行body。即使
开始时condition是伪,body也会被执行一次。

下面的例子会印出每个输入记录十次。

gawk'{i=1

do{

print$0

i++

}while(i<=10)

}'

for(initialization;condition;increment)

body

此叙述开始时会执行initialization,然後只要condition是真,它

会重复执行body与做increment。

下面的例子会印出每个输入记录的前三个栏位。

gawk'{for(i=1;i<=3;i++)

print$i

}'

break会跳出包含它的for、while、do-while循环的最内层。

下面的例子会找出任何整数的最小除数,它也会判断是否为质数。

gawk'#findsmallestdivisorofnum

{num=$1

for(div=2;div*div<=num;div++)

if(num%div==0)

break

if(num%div==0)

printf"Smallestdivisorof%dis%d/n",num,div

else

printf"%disprime/n",num}'

continue使用于for、while、do-while循环内部,它会跳过循环体的剩余部分
,立刻进行下一次循环的执行。

下面的例子会印出0至20的全部数字,但是5并不会被印出。

gawk'BEGIN{

for(x=0;x<=20;x++){

if(x==5)

continue

printf("%d",x)

}

print""

}'

next语句强迫gawk立刻停止处理目前的行而继续下一个输入行。

exit语句会使得gawk程式停止执行而跳出。然而,如果END出现,它会去执
行END的actions。


自定义函数

你可以定义自己的函数,其格式是

functionname(parameter-list){

body-of-function

}


name是所定义的函数名字。parameter-list是函数的变量列表。变量间使用逗号分
开。

函数可以在程序的任何地方定义,不过习惯上总是定义在程序的开头部分。

下面这个例子,会将每个记录的第一个栏位之值的平方与第二个栏位之值的平方加
起来。

{print"sum=",SquareSum($1,$2)}


functionSquareSum(x,y){

sum=x*x+y*y

returnsum

}

如果你熟悉任何编程语言,那么掌握awk都是很轻松的事情,如果你不喜欢它,那么你
可以参考我们下面介绍的perl。

你可能感兴趣的:(grep)