sed与awk

ftp://ftp.oreilly.com/published/oreilly/nutshell/sedawk_2

 有3种方法可以在命令行上放多个指令:

1. 用分号分隔指令
sed 's/ma/, mutil/;s/zhao/zzzz/'
2, 使用-e
sed -e 's/fs/fsgf/' -e 's/dfdg/dhs/'
3. 使用分行指令功能
    sed '
s/g/dfgh/
s/zz/dfgf/
s/CA/,FG/' list

4. sed / awk -f scriptfile file

sed和awk指令结构相同,都是由模式和过程组成,并且都可以使用多个指令。在awk中语句和函数取代了一个或两个字符组成的命令序列,例如print语句打印输出。

 awk出错信息

1. 没有用大括号{},将动作括起来。
2. 没有用单引号' '将指令括起来。
3. 没有用斜杠//将正则括起来。

 awk引用外部变量http://www.xuebuyuan.com/105422.html

awk在使用某个变量前不必先赋值,因为awk将变量复制为空字符串。

正则相关

1. 在awk中点号.可以匹配换行。
2. [...]方括号中^表示取反,即取其他字符,awk中包括换行。第一个字符为右括号]或-时表示它是成员,同事存在时]放第一个,-放最后。其他元字符在方括号中失去原来的含义,反斜杠\在awk的方括号中仍有特殊意义,可以转义连字符或方括号,如[a\]1]。
3. {n,m}在有些awk中仍然不能使用。awk支持egrep中的大多数正则用法。扩展正则:+, *, |, (), {},
4. .[!?;:,".] . 第一个和最后一个点是通配。
5.
POSIX字符类及意义:  必须出现在方括号表达式中,如[[:upper:]]
[:upper:]  表示大写字母[A~Z]
[:lower:]  表示小写字母[a~z]
[:digit:]  表示阿拉伯数字[0~9]
[:alnum:]  表示大小写字母和阿拉伯数字[A~Za~z0~9]
[:space:]  表示任何产生空白的字符,包括空格或Tab键等
[:alpha:]  表示大小写字母[A~Za~z]
[:cntrl:]  表示键盘的控制按键,包括Tab、Del等按键
[:graph:]  表示除了空格符(空格键与Tab键)外的其他所有按键
[:print:]  表示任何可以被打印出来的字符
[:xdigit:] 表示十六进制数字[0~9A~Za~z]
[:blank:]  表示空格键与Tab键
[:punct:]  表示标点符号,包括:" ' ? ! ; : # $... 
基本sed命令
sed命令可以指定0个,1个,或2个地址。每个地址都是一个描述模式,行号或者正则表达式。 注: 行号计数器不会因为多个输入文件重置。 如果指定了逗号分隔的两个地址,那么命令应用于第一个地址的第一行和它后面的行,直到匹配第二个地址的行(包括此行)。第二个地址从第一个地址之后开始寻找。第一个地址启用动作,第二个地址禁用动作,无法先行决定第二个地址是否会匹配,一旦匹配了第一个地址,这个动作就应用与这些行。直至第二个地址匹配。 如果地址后有感叹号
!,那么命令将应用与不匹配该地址的所有行。 /^\.TS/,/^\.Te/!d 删除这些行之外的所有行。

$可以表示最后一行,但不应该和正则表达式中的$混淆。正则必须封闭在斜杠(/)中。 /^$/d

命令后添加空格会产生语法错误,命令的结束必须在行的结尾处。
如果命令之间用分号分隔,那么可以讲多个sed命令放在同一行。 n;d在结构上是正确的,然后在n后面放置一个空格会导致语法错误,而在d前面放置一个空格是可以的。不提倡在同一行放置多个命令。
分组命令
linux sed命令详解 http://www.cnblogs.com/ggjucheng/archive/2013/01/13/2856901.html
http://www.cnblogs.com/stephen-liu74/archive/2011/11/17/2245130.html
sed使用大括号{}将一个地址嵌套在另一个地址中,或者在相同的地址上引用多个命令。 如果想指定行的范围,然后再这个范围内再指定另一个地址,则可以嵌套地址。注:左大括号必须在行末,而且右大括号本身必须单独占一行。要确保在大括号之后没有空格。
/^\.TS/,/^\.Te/{          注:大括号可以在同一行上,命令之间用分号隔开。sed -n '/root/{n;d}' /etc/passwd #将匹配root行的下一行删除
/^$/d  #命令在大括号内可以缩进
s/zhaodfd/.ps 8/
s/^\.vs 2/.ps 10/  #替换部分的点号不必转义。
}
替换
[address]s/pattern/replacement/flags
flags可以是:n(第n次), g, p, W file(写入文件)
分隔符可以是任意字符,出现3次并且在replacement之后是必须的。s!/home/z!/home/x!
在replacement部分只有下列字符有特殊含义:
&:正则匹配的整个内容
\n : 第n个字符串,在pattern中用\(。。。\)指定。
\ :转义字符。&符号,反斜杠\,定界字符,换行都可以用\转义。因此除了正则中的元字符外,sed的替换部分也有元字符。
echo 'zh xng' | sed 's/ /\    #转义换行,\&    注: 上换行用\n也可以
xxxxxxxx\      #换行
yyyyyyyyyy/'
注: 如果匹配同一行的多个替换命令,那么该行的多个备份就会被打印或者写到文件中。
删除
d,删除整行,而不是匹配部分。一旦执行删除命令,那么在“空的”模式空间中就不会再有命令执行。删除命令会导致读取新的输入行,而编辑脚本则从头开始新的一轮(这一行为和next命令相同),UNIX文件写到:不允许在被删除的行上进一步操作。
追加,插入,更改
[line-address]a\, [line-address]i\, [address]c\. 命令后面接反斜杠或者空格都可以。但是用反斜杠才是正路,很多书籍和命令详解都用的是反斜杠。
[root@tg output]# echo -e 'zhao xiang dsgf fsad  ssdf sdgfdfsgd' | sed '/zhao/a\ ni hao'  
#斜杠后面的任何内容追加在匹配行之后,即使是命令 /xian/i\ first line也被当做文本。空格也原样追加。
[root@tg output]# echo -e 'zhao xiang dsgf fsad ssdf sdgfdfsgd' | sed '/zhao/a\ ni hao' zhao xiang dsgf fsad ssdf sdgfdfsgd ni hao [root@tg output]# echo -e 'zhao xiang dsgf fsad ssdf sdgfdfsgd' | sed '/zhao/a ni hao' #如果命令后面接空格,则多余的空格被忽略了。 zhao xiang dsgf fsad ssdf sdgfdfsgd ni hao
正确的做法应该是在命令后用反斜杠,然后追加的文本单独起一行,文本中有换行用\n或者用反斜杠换行。最后一行不用反斜杠。 这样即使有多个命令也不会有问题。
[root@tg output]# echo 'test  zhao xiang ' | sed '/^test/a\
--->this is a example
> /xiang/i\
> first line'
first line
test  zhao xiang 
--->this is a example
[root@tg output]# 
[root@tg output]# echo 'test  zhao xiang ' | sed '/^test/i\
> '

test  zhao xiang 
[root@tg output]#插入空行,让命令后面的行为空
更改命令清除模式空间,与删除命令有同样的效果,在更改命令之后的命令不应该被提供。
插入和追加命令不影响模式空间的内容。所提供的文本将不匹配后续命令的任何地址,那些命令也不影响该文本,不管什么更改改变了模式空间,所提供的文本仍然会正确输出。即使模式空间不是那样的,而且,所提供的文本不参与sed的内部计数器

 列表命令

列表命令l 用于显示模式空间的内容,并将非打印字符显示为两个数字的ASCII码。类似于vi的列表命令(:l)

sed -n -e "l" test
因为列表命令立即产生输出,所以抑制默认的输出,否则会得到行的重复。

 转换

[address]y/abc/xyz/ 影响模式空间的所有内容

打印行号

[line-address]= , 这个命令不能对一个范围的行进行操作。

下一步

[address]n , 输出模式空间的内容(如果没有抑制输出)然后读取下一行,替换模式空间,而不返回到脚本的顶端,之前的命令应用于当前行,后续命令应用于替换之后的内容。

读和写

[line-address]r file, [address]w file . 读命令将由fiel指定的文件确定的行之后的内容读入模式空间,不能对行范围操作。写命令将模式空间的内容写入文件。文件名之前必须有空格,空格后到换行符前的内容都是文件名。 文件不存在也不会报错。如果一个脚本有多个命令写到同一个文件中,那么每个命令的内容将追加到文件中。而且每个脚本最多只能打开十个文件。

使用-n可以阻止模式空间的行被自动输出,但是读命令的结果仍然转到标准输出。

退出

退出命令[line-address]q, 会使sed停止读取新的输入行,并停止将他们送到标准输出。它只适用于单行地址,一旦找到匹配行,脚本就结束。

 

awk

 echo a b c | awk 'BEGIN {one = 1;two=2} {print $(one+two)}'  #输出c

 分隔符:

awk -F"\t" 或者 BEGIN{ FS=","} ,可以使用正则。 输出分隔,OFS="\t",OFS可以重新定义为一系列字符。

print $1,$2  逗号使个字段之间用空格隔开。不用逗号则各字段连在一起,print $1 "," $2  ,输出"$1逗号$2"。 输出域的分隔符默认是一个空格,保存在OFS中。如$ awk -F: '{print $1,$5}' test,$1和$5间的逗号就是OFS的值。

awk中RS,ORS,FS,OFS区别与联系

变量

awk中可以使用转义序列。变量,运算符,表达式,控制结构和C相似。变量无需加$符号,未初始化的变量为空或0。  每个变量有字符型值和数值型值,不包含数字的字符串为0.

空格是字符串连接操作符。Z="hello" "world"

赋值操作符:++,--,[+-*/%^]= ,**=  ,关系操作符:>,<,>=,<=,==,!=, ~, !~ 匹配/不匹配。逻辑操作&&,||,! ,!可以用于字符串,判断字符串是否非空。 见awk编程: 复合表达式,都和C类似。 匹配可以用于字符串变量也可用于/regex/. name=root; $1 ~ name 或者 $1 ~ /root/  貌似用正则的地方也可以用字符串。

in操作符可以判断某个下标是否在数组中。条件操作 expr ? action1 : action2

改变当前记录的字段数NF的值会有副作用,增加NF的值会创建新的空字段,并重新建立$0,字段由OFS的值分隔。

变量名    变量内容
ARGC    命令行参数的数量。
ARGIND    命令行正在处理的当前文件的AGV的索引。
ARGV    命令行参数数组。
CONVFMT    转换数字格式。
ENVIRON    从shell中传递来的包含当前环境变量的数组。
ERRNO    当使用close函数或者通过getline函数读取的时候,发生的重新定向错误的描述信息就保存在这个变量中。
FIELDWIDTHS    在对记录进行固定域宽的分割时,可以替代FS的分隔符的列表。
FILENAME    当前的输入文件名。
FNR    当前文件的记录号。
FS    输入分隔符,默认是空格。
IGNORECASE    在正则表达式和字符串操作中关闭大小写敏感。
NF    当前文件域的数量。
NR    当前文件记录数。
OFMT    数字输出格式。
OFS    输出域分隔符。
ORS    输出记录分隔符。
RLENGTH    通过match函数匹配的字符串的长度。
RS    输入记录分隔符。
RSTART    通过match函数匹配的字符串的偏移量。
SUBSEP    下标分隔符。
[zhangy@localhost test]$ awk 'BEGIN{OFS="|";}{print $1,$2}' test1
 111|222
 333|444
 555|666
[zhangy@localhost test]$ awk 'BEGIN{OFS="|";}{print $1 OFS $2}' test1
 111|222
 333|444
 555|666
[zhangy@localhost test]$ awk 'BEGIN{OFS="|";}{print $0}' test1
 111 222
 333 444
 555 666
[zhangy@localhost test]$ awk 'BEGIN{OFS="|";}{NF=NF;print $0}' test1
 111|222
 333|444
 555|666
为什么第二种方法中的OFS生效呢?个人觉得,awk觉查到列有所变化时,就会让OFS生效,没变化直接输出了。 可以设置$1=$1

可以用NF来引用最后一个字段。 $NF,$(2+3)

多行记录

FS="\n"; RS="" 记录分隔符为空字符串,代表空行。 

OFS="\n"; ORS="\n\n"; 

格式化打印 

printf(format,[args]) , 圆括号可选。 Shell常用技巧(五) awk编程 , Awk学习笔记 ,Shell编程-awk

format的格式:%-width.precision specifier , 数值的默认precision为"%.6g" ,默认精度可以通过设置系统变量OFMT来改变,如"%.2f"。

向脚本传递参数

awk引用外部变量

控制结构,和C相同,if [else if] else, for(;;),while(){}, do{}while(),break,continue

next导致读入下一个输入行,并返回到脚本底部,可以避免当前输入行执行其他操作  。 exit是输入主循环退出,转到END规则。没有END或者在END中用exit则终止脚本的执行。exit可以使用一个表达式作为参数,作为awk的退出状态返回。

数组

a[sub]=value,不必指明数组大小,awk的所有数组都是关联数组,下标可以使字符串和数字。

遍历:for( var in arr )do something with arr[var] ,访问顺序随机。 eg: arr[grade]++ ,即使grade最初不在数组arr中。

重要的是记住awk中的所有下标都是字符串型的,即使使用数字作为下标,awk自动将它们转为字符串,当使用浮点数做下标时向字符串转可能会有影响。

成员测试: item in array, 存在返回1,否则返回0。   awk应用小结(所有命令行均经调试) 

split()创建数组: n=split(str, array, sep), 返回元素个数,sep可以是正则。

删除数组元素: delete array[sub]

模拟多维数组: array[3,2], 成员测试时下标必须放置在圆括号中。 if((i,j) in array)。 多维数组的下标分别解释为单独的字符串,用系统变量SUBSEP连接,默认为“\034”。

多维数组循环: for(item in array) split(item,sub,SUBSEP). 必须用split()函数来访问单独的下标分量,split使用下标item创建数组sub。

系统变量的数组: 命令行参数ARGV,下标0到ARGC-1,不包括脚本名和选项,ARGV[0]是'awk'。  ENVIRON环境变量数组,其中下标是环境变量的名字。

 可以向ARGV数组添加元素,但是注意要同时增加ARGC的值,awk使用ARGC的值来得到ARGV中有多少元素可以处理。如果ARGV的元素值是一个空串awk将跳过它。

函数

数学函数...

取整int(),print int(100/3)

rand()生成0到1之间的浮点随机数。srand()为随机数发生器设置一个种子数或者起点数,srand([x]),不带参数是使用当前时间。

字符串函数:字符串的起始位置为1,

sub(regex,s,[t=$0]),替换第一个出现的,成功返回1否则返回0. gsub(regex,s,[t=$0])返回替换的个数。正则用//包围。 直接改变指定字符窜,返回的不是替换后的字符串。 和sed一样,替换字符串中的&将被正则匹配的字符串代替。\&代表"&",在字符串中必须输入两个\\。

index(s,t)返回t在s中的位置,没有指定s返回0. length([s=$0])。

match(s,regex),正则用//包围,返回regex出现的起始位置,若没有,返回0。设置RSTART和RLENGTH的值。可以用在while循环中,但在每次循环中要改变已匹配的[RSTART..RSTART+RLENGTH]部分,使下次循环是不再匹配已匹配的部分。   注意sub的正则在前面,match的正则在后面。

split(s,arr,sep)返回元素个数,没有sep则用FS。

sprintf(fmt,ecpr),  substr(s,p,[n]),子串p开始最大长度为n。 tolower(s), toupper(s)。

lowerChars="abcdef..."; f=substr($1,1,1); if(i=index(lowerChars,f)) $1=substr(upperChars,i,1) substr($1,2) #首字母改大写

awk 用法 Awk 命令学习总结 Linux Shell常用技巧(五) awk编程 awk使用手册

自定义函数

function name(arg1,arg2,..){ statements }  return 语句返回。

函数参数都是局部变量。 函数体重定义的变量默认为全局变量。 可以将局部的临时变量列在参数列表末尾。 在参数列表中参数按顺序接受传递来的值。其他的参数初始化为空串。

高级主题

getline函数用于从输入中读取另一行。也能处理来自文件和管道的输入。next语句也是读取下一行,但是next语句将控制传递到脚本的顶部。getline函数得到下一行但不改变脚本的控制。返回值,1:读取一行, 0:到达文件末尾, -1:遇到错误。 尽管被称为一个函数,但是它类似于一个语句,且不能写成getline()。   while(getline > 0) cmd=amd $0

当读取新行后,getline将它赋给$0,并将它分解成字段,同时设置NF,NR,FNR。因此新行变成当前行。如果需要,可以用getline读取一行并将它赋给一个变量从而避免改变$0。 getline var

从文件中读取:while( (getline < "data") > 0 ) ,"data"是一个文件名字符串。加括号是为了防止混乱。

标准输入读取: getline [var] < "-"

将输入赋值给变量:getline var 不是var = getline, 对$0没有影响。输入行没有被分解为字段,对NF没有影响。但它递增了记录计数器NR, FNR。

从管道读取: "who an i" | getline .将命令的结果赋给$0,同时设置系统变量。$0被改变,$1,NF等变量肯定都跟着改变,它也是根据FS来分隔字段,所以要注意改变FS的地方。 即使$0不改变,NR, FNR也会改变。

当包含多个输入行时getline一次读取一行, 要读取所有行就必须创建一个循环来执行getline直到不再有输出为止。while("who" | getline)...注意:其中who命令值执行一次,但是getline则调用多次。

 close()函数: 关闭打开的文件或管道。

因为:1. 每次只能代开一定数量的管道。 通常用在getline返回0或-1是,如close("who"). 2. 关闭管道使得你可以运行用一个命令两次。 3.为了得到一个输出管道来完成它的工作,使用close()可能是必要的,{some processing of $0 | "sort > tmpfile"}  END{close("sort > tmpfile")}

system()函数执行命令。然而它没有产生可供程序处理的输出。它返回被执行命令的退出状态。 if(system("mkdir dale") != 0) print "failed!"

 直接向文件或管道输出:

print和printf可以用输出重定向操作">"和">>"将结果直接写入到文件中。  print > data.out 将记录写入data.out

print | command 直接暑促到管道。该命令在第一次执行时打开一个管道,并将当前记录作为输入传送给命令,换句话说这里的命令只执行一次,但每执行一次print将提供另一个输入行。

posted @ 2014-11-04 17:40  赵翔  阅读(209)  评论(0编辑  收藏  举报