为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

Awk one-liners explained 中文版

2012-03-27 25页 pdf 495KB 41阅读

用户头像

is_060295

暂无简介

举报
Awk one-liners explained 中文版 Awk one-liners explained 中文版 http://bbs.chinaunix.net/thread-1640657-1-1.html 首先声明,这篇文章并不是原创,这是我在学习 awk 的过程中,经 CU 的朋 友推荐,看到了 Peteris Krumin 关于 awk 的非常精彩的讲解,由于原文是英文版 的,英语水平稍差的朋友可能学习起来会有点困难,为了能够给正在学习 awk 的朋友们提供一点点帮助,也锻炼一下自己的英语水平,我将这些 awk 讲解翻 译了一下,加上了一点个人的看法,由于...
Awk one-liners explained 中文版
Awk one-liners explained 中文版 http://bbs.chinaunix.net/thread-1640657-1-1.html 首先声明,这篇文章并不是原创,这是我在学习 awk 的过程中,经 CU 的朋 友推荐,看到了 Peteris Krumin 关于 awk 的非常精彩的讲解,由于原文是英文版 的,英语水平稍差的朋友可能学习起来会有点困难,为了能够给正在学习 awk 的朋友们提供一点点帮助,也锻炼一下自己的英语水平,我将这些 awk 讲解翻 译了一下,加上了一点个人的看法,由于本人水平有限,错误肯定不少,欢迎高 手们批评指正,共同进步。谢谢。 http://www.catonmat.net/blog/awk-one-liners-explained-part-one/这是原文的链 接。。 废话不说,开始进入 awk 第一个部分:行距,编号和运算。 第一部分:行距,编号和运算 1、输出两倍行距文件 #more file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 #awk '1; { print "" }' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 这个是如何运行的呢?每一个 awk 程序都是由一系列的 “模式—动作”语句组 成,即“模式{动作}”,在这个例子中有两个这样的语句,即"1" 和 "{ print "" }", 每个语句中无论是模式还是动作都可能不存在。如果模式部分不存在,默认匹配 所有行,那么就对每一行都执行动作。如果动作不存在,默认执行动作{print}, 因此这个例子也可以写成这样: #awk '1 { print } { print "" }' file 只有当模式正确匹配,动作才会执行,由于“1”始终是正确的,所以这个例子 可以写成两条打印的语句 #awk '{ print } { print "" }' file 在 awk 中每一条打印命令中后面都跟了一个输出分隔符(ORS),默认是换 行。第一个打印命令后面没有加参数,等同于{print $0},$0 是相应的每一条记录, 是可变的,通过设置输出记录分隔符(ORS)可以得到不同类型的记录。第二个 打印命令后面接的是“”(空),我们知道每个打印命令后面都跟了一个输出记录 分隔符,实际上打印了一个新行,因此打印出来以后每行之间有双倍的行距。 PS:例子中的分号的作用是将两条语句区分开来,如果不用分号 awk 会认为是 一个语句。其他两种写法不加分号是因为到有动作{print}存在,后面的内容 会自动认为是另一个语句。所以在 awk 中,当前一个语句只有模式而没有动作 时,后面要在加语句的话,必须要用分号区分开来,有动作时分号可有可无。 2、另一种方法输出两倍行距文件 #awk 'BEGIN { ORS="\n\n" }; 1' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 BEGIN 是一种特殊的语句,这种语句不检测输入文件。也就是在读输入文件之前 运行。通过设置输出记录分隔符(ORS)为两次换行的方法实现输出两倍行距文 件。根据之前提到过的,语句“1”等同于{print},打印出来的每条记录之间的分 隔符都为前面设置的 ORS。 PS:一个完整的 awk 语句应该是这种形式 awk ‘BEGIN{动作};模式{动作};模式{动作}。。。;END{动作}’ file 其中 BEGIN{动作}和 END{动作}分别是在读输入文件之前和读输入文件之后执行, 通常用来制表和统计数据,很多时候都不必用到。只有中间的语句模式{动作}才 会读输入文件并对其执行动作。 3、输出两倍行距文件,并且任意两行之间只有一个空行存在。 首先修改一下 file,在里面加一个空行 #more file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 #awk 'NF { print $0 "\n" }' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 这个命令使用了另一个 AWK 变量 NF(域的个数),意思是当前行被分割的数量, 比如 234532 23456 555555 这一行被分割成 3 个部分,因此得 NF 的值就是 3,空行的无法被分割,因此 NF 的大小就是 0.在模式中使用 NF 可以有效的过滤空行。这个命令的意思是:只要 行中存在域,就在此行的后面打印一个空行。 PS:当模式为数值(-1,0,1,1.1)时,只要数值不为 0,即为匹配所有行。 4、 三倍行距 #awk '1; { print "\n" }' file 12 32423 -2354235 234532 23456 555555 -432346 45435 4462 这个命令跟之前的很是相似,语句“1”等同于{print},因此也可以写成 #awk '{ print; print "\n" }' file 先打印一行,然后是打印输出记录分隔符(ORS),默认是换行。 PS:有些初学者在这里可能会有点疑惑(我刚开始也想了很久 O(∩_∩)O),不 过仔细想想就容易理解了。之前我们提到过,每个打印命令后面都跟了一个输出 记录分隔符(默认是换行),因此在这个命令中,先执行第一个语句:首先打印 文件的第一行,然后跟一个 ORS,也就是换行了,接着执行第二个语句{print “\n”}, \n 就是换行,这时候后面又跟一个 ORS,还是换行,因此出现了在每行之间出现 了 2 个空行。 5、给每个文件的行单独编号 #awk '{ print FNR "\t" $0 }' file 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 这个 awk 程序在每行之前附加了一个文件行号(FNR)file line number 和一个 tab (\t),FNR 包含了每一个文件当前行的行号。比如说,awk 针对两个文件做操作: #awk '{ print FNR "\t" $0 }' file file 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 可以看到,结果是分别给两个文件的每一行之前加上该行在文件中的行号。FNR 给文件的行编号时,如果有多个文件会重新开始编号。 6、给所有文件的行一起编号 #awk '{ print NR "\t" $0 }' file file 1 12 32423 -2354235 2 234532 23456 555555 3 -432346 45435 4462 4 12 32423 -2354235 5 234532 23456 555555 6 -432346 45435 4462 这个命令和第五个例子几乎一样,唯一不同的地方是使用了参数(行号)NR-Line Number。NR 与 FNR 不同的地方就在于 NR 在给多个文件的行编号的时候不会根 据文件重新编号,而是按照读取顺序统一编号。 7、花式编号 #awk '{ printf("%5d : %s\n", NR, $0) }' file 1 : 12 32423 -2354235 2 : 234532 23456 555555 3 : -432346 45435 4462 这个命令用了通常格式 printf()函数来给给行编号,像普通的 printf()函数一 样格式化参数。这里需要特别注意的是在 printf()函数后面不会附加一个输出 记录分隔符(ORS)。因此我们需要在每一行的后面明确的打印出一个换行符(\n)。 这个命令的结果是在每行之前打印行号和一个冒号。 8、只给非空行编号 #more file 12 32423 -2354235 234532 23456 555555 432346 45435 4462 #awk 'NF { $0=++a " :" $0 }; { print }' 1 :12 32423 -2354235 2 :234532 23456 555555 3 :-432346 45435 4462 Awk 参数都是动态的,在第一次使用的时候建立。这个命令指定 a 这个变量随着 行数的增加和不断自增长,空行除外(NF=0)。然后将这个参数的值和冒号附加 在每一行的开头并打印出来。 PS:在这个例子中,第一个语句是 NF{$0=++a”:”$0},模式是 NF,动作是一个 赋值语句$0=++a”:”$0,即当 NF 不为 0 的时候,在每一行的开头都加上一个变 量 a 和一个冒号,然后通过第二个语句打印出来。记住,在这里“=”不是等于 的意思,而是给$0 重新赋值,“==”才是等于的意思。 9、计算文件行数(与 wc –l 的作用类似) #awk 'END { print NR }' file 4 前面提到过,END{}是一种不测试文件的特殊语句,它是在所有行遍历完以后执 行。在所有行遍历完以后,NR 就等于最后一行的行号,再出变量 NR 的值,就 是输出文件的行数了。 10、打印每行中域值的总和 #awk '{ s = 0; for (i = 1; i <= NF; i++) s = s+$i; print s }' file -2321800 813543 -382449 awk 有很多地方借鉴了 c 的风格,比如这个 for(;;){„}循环。这个命令使用 for 循环遍历了每一行中的每个域(NF 即为每行中域的个数)。然后将每个域的值累 加给变量 s,最后打印出 s 的值,即为所有域相加后的值。 PS:如果把 s=s+$i 写成 s=s+i,则结果大相径庭 #awk '{ s = 0; for (i = 1; i <= NF; i++) s = s+i; print s }' file 6 6 6 因为前面的 s=$1+$2+$3,而后面的 s=1+2+3。 11、打印所有行中域值的总和 #awk '{ for (i = 1; i <= NF; i++) s = s+$i }; END { print s+0 }' file -1890706 这个命令基本上和#10 的一致,不同的是打印出来的结果是所有域值的累加。注 意到开始没有初始化变量 s 的值为 0.这是因为要将所有行的每一个域相加的话, 就不能在遍历每一行的时候将 s 初始化为 0,否则最后得到的 s 就是最后一行的 所有域相加的值,而不是所有行。还需要注意的是最后是{print s+0}而不是{print s}。 当文件 file 中没有域(都是空行)的时候这是很有必要的。因为如果没有域的话, 那么变量 s 就无法建立也没有被定义,输出一个没有定义的变量就等于什么都不 输出(ORS 还是要跟的)。加上一个 0 的话就可以从数值上体现 s 的大小即为 0。 12、将所有域都替换成它的绝对值 #awk '{ for (i = 1; i <= NF; i++) if ($i < 0) $i = -$i; print }' file 12 32423 2354235 234532 23456 555555 432346 45435 4462 这个命令也借鉴了 c 的两个特征,if(..){„}语句和省略了大括号。这个命令遍历 了所有行中的每一个域,检查是否有小于 0 的域,如果有,将域值取反,变成整 数。域值可以使用参数间接的赋值,比如 i=5;$i=”hello”,就是将第五个域赋值 为”hello” 下面是该命令的另一种比较完整的写法,在每行中所有的域都检查过并且重新赋 值以后,再执行打印的命令。 awk '{ for (i = 1; i <= NF; i++) { if ($i < 0) { $i = -$i; } } print }' 13、计算一个文件中所有域的数量 #awk '{ total = total + NF }; END { print total+0 }' file 9 这个 awk 程序遍历文件 file 所有的行,并将每一行的域的数量累加起来,并将累 加的数量赋给变量 total,一旦文件遍历完,开始执行 END{},也就是打印出变量 total 的值,通过第十一个例子我们可以知道为什么打印的是 total+0。 14、打印包含“55”内容的行的个数 #awk '/55/ { n++ }; END { print n+0 }' file 1 这个命令包含两个模式{动作}语句。第一个是/55/{n++}。模式中两个斜杠之间的 是一个正则表达式。表示匹配所有包含数字“55”的行(不一定精确匹配 55 这 个数字,也匹配像 555,553,255,55d 这种)。当匹配了一行时,变量就自动+1, 第二个语句是是 END{print n+0},表示当文件遍历完后,打印出变量 n 的值。注 意到是 print n+0,因为当没有行匹配 55 的时候,n 就没有被创建和被定义,n 的值也就打印不出来,加上 0 可以避免无输出。 PS:关于正则表达式有一篇文章介绍的不错: http://bbs.chinaunix.net/viewthread.php?tid=63273 可以参考一下。 15、找出第一个域中最大的数 #awk '$1 > max { max=$1; maxline=$0 }; END { print max"\n"maxline }' file 234532 234532 23456 555555 这个命令将通过比较将第一个域中的最大数保存在变量 max 中,并且把相对应 的行赋给变量 maxline,当所有行遍历完以后,把它们都打印出来。需要注意的 是,当第一个域中所有的域值都是负数时,这个程序无法工作。 PS:为什么第一个域都是负数的时候,打印不出来任何东西呢,是因为当第一个 域都是负数的时候,没有行能够匹配模式$1>max,因此后面的动作也就不能执 行,变量 max 和 maxline 没有被定义,因此打印不出任何东西了。 利用下面的命令可以弥补这一缺陷 #awk 'NR == 1 { max = $1; maxline = $0; next; } $1 > max { max=$1; maxline=$0 }; END { print max”\n” maxline }' file -12 -12 32423 -2354235 这个命令在前一个命令的基础上加了一个命令 NR==1{max=$1;maxline=$0;next}, 我们来看一下,模式部分是行号 NR==1,也就是说动作只对第一行做操作,把第 一个域的值赋给变量 max,第一行赋予变量 maxline。注意后面的 next 函数。next 意思就是:匹配 NR==1 的行执行完动作{max=$1;maxline=$0}后,后面的语句通通 不执行(END{}除外),也就是说后面的语句从 NR=2 开始执行。通过后面语句来 和第一行的$1 做比较,最后选出最大的$1 和相应的行并打印出来。 PS:新加的语句主要是用来初始化变量 max 和 maxline,即不管$1 的大小,直接 把$1 赋给 max,然后再比较。因此不会出现 max 没有创建和被定义的现象。 16、在每行之前打印出域的个数 #awk '{ print NF ":" $0 } ' file 3:-12 32423 -2354235 3:234532 23456 555555 3:432346 45435 4462 这个命令还是很简单了,先打印出预先确定的 NF-number of fields(域的数量),后 面加一个冒号和行记录。 17、打印每行的最后一个域 #awk '{ print $NF }' file -2354235 555555 4462 每一行域的数量 NF 不会总是一样,$NF 就是每行的最后一个域。 18、打印最后一行的最后一个域 #awk '{ field = $NF }; END { print field }' file 4462 这个命令将记录中的最后一个域赋给变量 field,所有行遍历完以后,变量 field 的值就是最后一行记录的最后一个域了,然后打印出变量 field。 有一个更好,更常用的写法 #awk 'END { print $NF}' file 4462 19、打印超过 4 个域的行 #awk 'NF > 4' file 这个命令省略了动作只有模式,缺省的动作就是{print $0},因此如果匹配到有超 过 4 个域的行,打印出来,如果没有,则无输出。 20、打印最后一个域值大于 4 的行 #awk '$NF > 4' file 234532 23456 555555 432346 45435 4462 和前一个例子不同的地方在于,这个模式匹配的是最后一个域值大于 4 的行。并 打印相关的行。 好了,第一部分内容写完了,比较基础的东西。 第二部分是文本转换和替代,下次继续! 第一部分的还是比较简单的,从第二部分开始,就要接触到很多函数和数组 的东西,由于本人只有一点点 c 的基础,所以为了不误人子弟,希望大家能够多 提。谢谢。 第二部分:文本编辑替换 21、将 windows/dos 下的回车换行转换为 unix 下的换行(unix 下运行) #awk '{ sub(/\r$/,""); print }' file 这个命令使用了 sub{regex,repl,[string]}函数,这个函数在字符串[string]中查找第 一次匹配正则表达式 regex 的字符(串),找到后用字符(串)repl 将这个匹配的 字符(串)替换。如果函数中没有[string],那么默认将使用$0(也就是整行)作 为要查找的对象。 这个命令将每行结尾的回车符/r 替换成“”,也就是将每行结尾的回车符删除。 打印命令将每行打印出来并在后面附加了一个 ORS(默认就是换行\n),这样结 尾的回车换行就变成了直接换行。 22、将 unix 下的换行转换为 windos/dos 下的回车换行(unix 下运行) #awk '{ sub(/$/,"\r"); print }' file 这个命令也同样使用了 sub()函数,这次将每行中的零字符的$(代表每行的末 尾)替换成回车\r,实际上就是在每行的结尾附加了一个回车符。打印记录的后面 也要加上 ORS(默认\n)。 23、将 unix 下的换行转换为 windos/dos 下的回车换行(windows/dos 下运行) #awk 1 file 这个命令可能执行,也可能不执行,取决于执行过程,如果在执行中从读取的文 件中发现了 unix的换行,程序会一行行的读取文件然后把换行 LF替换成windows 下的 CRLF(回车换行),如果没有检测到 unix 的换行符,程序会打印出整个文件 然后再以 CRLF 结束。 24、将 windows/dos 下的回车换行转换为 unix 下的换行(windows/dos 下运行) # gawk -v BINMODE="w" '1' file 理论上来说,这个命令应该会在 dos 上将 CRLF 转换为 LF,在 GNU awk 的说明中 这么一段话:在 dos 命令行下,gawk 会默认将输入文件的每行结尾的“\r\n”转 换成“\n”或者输出的行结尾“\n”转换为“\r\n”。特殊参数 BINMODE 允许用以下的值控制这些转换,如果 BINMODE 的值为 W,二进制模式为写(写 的时候不转换)。 建立在 windows 下用下面的命令实现该功能 # tr -d \r tr 程序用来转换字符,-d 选项是用来删除不需要的字符,而“\r”正是每行结尾 的回车符,因此把输入文件的每行结尾“\r\n”变成了“\n” PS:23,24 都是在 windows/dos 下运行,我没有试过,翻译的也很勉强,而且也不 懂,为了不误导别人,请高手们不吝赐教!! 25、删除每行开头的空白(空格或者制表符 tabs) #more file aaaa aa 234532 23456 555555 -12 32423 -2354235 # awk '{ sub(/^[ \t]+/, ""); print }' file aaaa aa 234532 23456 555555 -12 32423 -2354235 这个例子同样使用了 sub()函数,将开头的空白部分替换成“”,也就是空。 正则表达式^[ \t]+ 的意思是行的开头匹配一个或多个空格或者 tabs,+就是指的 一个或多个字符的意思。 26、删除每行结尾的空白(空格或者制表符 tabs) # awk '{ sub(/[ \t]+$/, ""); print }' file 跟上面的例子如出一辙,不过删除的是末尾的空格或 tabs,^和$都是一个空字符, 表示位置,一个是行的开头,一个是行的结尾。 27、同时删除开头和结尾的空白 # awk '{ gsub(/^[ \t]+|[ \t]+$/, ""); print }' file 这回用的是 gsub()函数,与 sub()一样,也是替换的作用,不一样的地方在于, sub()替换的是一个匹配的字符,而 gsub()是全局替换,也就是替换所有, 比如说有一个变量 f=foo,sub("o", "x", f)替换后的结果是 fxo,而 gsub("o", "x", f) 替换后的结果是 fxx。 这个命令的正则表达式部分同时匹配每行的开头和结尾的空白,|的意思是或者, 因此无论是开头还是结尾的空白都将被删除。 要删除每个域之间的空白可以用下面的命令实现 #more file aaaa aa 234532 3456 555555 -12 32423 -2354235 # awk '{ $1=$1; print }' file aaaa aa 234532 3456 555555 -12 32423 -2354235 这个命令有点复杂,乍一看$1=$1 好像什么都没做是吗,其实不然,在 awk 里面, 当你改变一个域的时候(比如给它赋值),awk 会重置$0,把所有的域拼接起来, 然后 print,而默认使用输出域分隔符 OFS 就是空格,这样所有域之间的空白都 将删除。 28、在每行的开头增加 5 个空白 # awk '{ sub(/^/, " "); print }' file aaaa aa 234532 3456 555555 -12 32423 -2354235 前面提到过,^只是代表每行的开头的一个空字符,即在每行的开头加上 5 个空 格。 29、右对齐文本 #awk '{ printf "%79s\n", $0 }' file aaaa aa 234532 3456 555555 -12 32423 -2354235 这个命令在$0 前添加空格直到整行的长度达到 79 个字节。更多关于 printf() 的使用方法请参考: http://www.gnu.org/manual/gawk/html_node/Basic-Printf.html 30、使文本居中 #awk '{ l=length(); s=int((79-l)/2); printf "%"(s+l)"s\n", $0 }' file aaaa aa 234532 3456 555555 -12 32423 -2354235 这个程序开始计算整行的长度,并把值赋给变量 l,length(var)函数返回字符 串 var 的长度,如果不指定字符串 var,返回的是整行$0 的长度。然后计算在每 行之前有多少个空格,也把这个值赋给变量 s,最后打印出文本,使得文本刚好 居中在 79 个字符中间,前面用空白填充。 31、替换文本 #awk '{ sub(/55/,"xx"); print }' file aaaa aa 234532 3456 xx5555 -12 32423 -2354235 这个替换命令把每行中的 55 替换成 xx,记住,这个命令只是替换第一次匹配的 字符。如果要把所有的 55 都替换成 xx,则用下面的命令 #awk '{ gsub(/55/,"xx"); print }' file aaaa aa 234532 3456 xxxxxx -12 32423 -2354235 可以看到所有的 55 都换成了 xx,还可以使用另一种替换的函数 #awk '{$0= gensub(/55/,"xx",2); print }' file aaaa aa 234532 3456 55xx55 -12 32423 -2354235 这个程序只是替换第二次匹配 55为 xx,使用的是之前没有见过的 gensub()函数, 这个函数的基本形式是 gensub(regex,s,h[,t]),这个函数查找在字符串 t 中第 h 次匹配正则表达式 regex 的字符,并用字符串 s 来替换。如果 t 没有指定,默认使用$0。 gensub()是一个非的函数,GNU 版本的 awk 或者 netbsd 系统中的 awk 有 此函数。 PS:这里需要注意的是,大家可以看到用 gensub()函数的时候是给$0 赋值, 而前面的 sub()和 gsub()并没有这样做,让我们看看不给$0 赋值是什么情 况 #awk '{gensub(/55/,"xx",2); print }' file aa aaaa 234532 3456 555555 -12 32423 -2354235 可以看到 print 出来的东西并不没有变化,这就是 gensub()和 sub(),gsub() 不同的地方之一,也就是 gensub 虽然对$0 做了替换,但是只保存在函数中,并 不对$0 做实际修改。因此如果想要把替换的内容输出,可以把 gensub()函数 赋值给$0。或者直接用{print gensub()}打印出结果。 32、在包含特定字符的行中替换字符 #awk '/34/ { gsub(/5/, "x") }; { print }' file aa aaaa 234x32 34x6 xxxxxx -12 32423 -2354235 在第一部分提到过,所有的 awk 程序都是由一个个的 模式{动作}语句组成,只 有当模式匹配的时候才执行动作。第一个语句的模式部分是/34/,即匹配包含 34 的行,如果有行包含 34,执行动作全局替换,将所有的数字 5 替换成字符 x,第 二个语句把所有行打印出来。如果你只想替换一次,用 sub()或者 gensub() 函数即可。 33、在不包含特定字符的行中替换字符 # awk '!/34/ { gsub(/5/, "x") }; { print }' file aa aaaa 234532 3456 555555 -12 32423 -23x423x 这个例子只是模式部分与前一个不一样,!就是非,也就是匹配不包含 34 的行对 其做替换操作并打印出所有的行。 34、将不同字符替换为同一个字符 # awk '{ gsub(/34|55|12/, "xx"); print}' file aa aaaa 2xx532 xx56 xxxxxx -xx 32423 -2354235 以上这 3 个例子讲的都是正则表达式,这个程序用管道符|实现匹配多个字符并 替换,最后打印整行。 35、倒序输出记录(类似 tac 的作用) # awk '{ a[i++] = $0 } END { for (j=i-1; j>=0;) print a[j--] }' file -12 32423 -2354235 234532 3456 555555 aaaa aa 这个程序时目前来说最复杂的一个了,首先把每行的记录都赋值给数组 a,比如 有两行记录“aa”和“aaa”,则分别是 a[0]=aa,a[1]=aaa。当文件的所有行都赋值 给了数组 a 以后,awk 执行 END{}版块,查找数组 a 中的元素并打印出来。在这 里有 4 行记录,因此 END # for (j = 4-1; j >= 0; ) print a[j--] 首先打印 a[3],然后是 a[2],a[1],a[0]。我们可以看到正好把所有行倒过来排列了。 36、把行结尾是反斜杠\的记录与下一行连起来 #more file aa aaaa 234532 3456 555555 \ -12 32423 -2354235 # awk '/\\$/ { sub(/\\$/,""); getline t; print $0 t; next }; 1' aa aaaa 234532 3456 555555 -12 32423 -2354235 这个 awk 程序先利用正则表达式“/\\$/”找出匹配每行结尾是\的记录,如果匹 配,首先用 sub{}函数将行尾的\删除,然后 getline 函数开始执行,这个函数的作 用是读取下一行的记录$0 并把$0 赋值给变量 t,Print $0 t 也就是打印当前的行(行尾的\已删除)和下一行(变量 t 即是下一行记录),然 后继续往下读取(已经赋值给 t 的那行不读取),第二个语句 1 等同与{print},打 印出结果。 很遗憾这个程序连接的记录不能超过两行(如果连续的第二行还是以\结尾,程 序并不会把下一行连接到这一行的结尾,这是因为 getline 以后,该行就不再读 取和操作了。)(我觉得最简单的方法就是将这个程序多执行几遍)。 37、打印出所有的用户名并排序 #awk -F ":" '{ print $1 | "sort" }' /etc/passwd 这是我们在程序中第一次看到-F 参数,这个参数的作用是自定义一个字符、字符 串、或者正则表达式把记录$0 分割成一个个的域$1、$2、$3.例如,如果有一行 记录为“ foo-bar-baz ” 同时 -F 设置为 “ - ” , 这行就被分割为 3 个域 $1=foo,$2=bar,$3=baz.如果不设置-F 参数的话,默认的 FS(记录分隔符)是空格, 因此这一行只有一个域,那就是$1=foo-bar-baz。 定义-F 参数和在 BEGIN{}模块中定义 FS 参数是一样的 #awk -F ":"等同于 awk 'BEGIN { FS=":" }' 因为/etc/passwd 包含了一系列系统用户,除此之外还有用户名,用户 id,组 id 等等,他们之间使用:分割开来,因此需要定义域分割符 FS 为冒号。 sort 命令的作用是排序,打印出用户名以后,使用 sort 将这些用户名按照英文字 母 a,b,c。。。的先后顺序排列。 38、打印第一第二个域并把它们反向排列 #awk '{ print $2, $1 }' file aa aaaa 3456 234532 32423 -12 这个程序比较简单,就是打印出每行的第二和第一个域。 39、把第一个域和第二个域互换 # awk '{ temp = $1; $1 = $2; $2 = temp; print }' file aa aaaa 3456 234532 555555 \ 32423 -12 -2354235 这个程序使用了一个临时的变量 temp,首先把$1 的值赋给 temp,然后把$2 的 值赋给$1,最后把 temp 的值(也就是之前$1 的值)赋给$2,正好把$1 和$2 的值 相互交换了,比如说输入是“foo bar baz”,那么输出即将是“bar foo baz”。 40、删除某个域 # awk '{ $2 = ""; print }' file aa aaaa 234532 555555 \ -12 -2354235 首先把空值“”赋给$2,在打印记录,相当于删除了$2。 41、在每行中以倒序的方式排列每个域 # awk '{ for (i=NF; i>0; i--) printf("%s ", $i); printf ("\n") }' aa aaaa 555555 3456 234532 -2354235 32423 -12 在第一部分我们知道 NF 变量是指每行中域的个数,在读取了记录以后,awk 把 每行域的数量指定给变量 NF,这个程序利用 for 语句将域反向打印出来,即 $NF,$(NF-1),..$1,然后再打印出一个换行符“\n”。 PS:有些朋友可能会有疑问,为什么打印函数不用 print 而是 printf{},我们先来 看用 print 的结果 # awk '{ for (i=NF; i>0; i--) print $i}' file aa aaaa 555555 3456 234532 -2354235 32423 -12 可以看到,打印出来的结果是每一个域后面都自动换行。前面提到过,因为 print{} 函数在打印一个域$i 后,会在后面自动附加一个记录分隔符 ORS,也就是换行, 这样就会导致没有安装记录输出。而 printf{}函数则不会再后面附加 ORS,第一个 命令是当一行中打印完所有的$i 后,再附加一个 ORS“\n”,这样就不会把原来的 记录打乱了。 42、删除连续出现的多余相同行(类似 uniq 的作用) #more file aa aaaa aaaa 234532 3456 555555 -12 32423 -2354235 # awk 'a !~ $0; { a = $0 }' file aa aaaa 234532 3456 555555 -12 32423 -2354235 我们前面提到过。Awk 中变量在被使用之前不需要初始化和公布。它们在第一使 用的时候创建。这个程序使用变量 a 保存上一行记录$0,然后再读取下一行,将 上一行的记录与下一行作比较.a!~$0 指的是,如果当前行不匹配上一行的记录, 表达式的值就是真,默认的动作就是{print},反之当匹配的时候,就不打印相关 的行,第二个语句又重新把当前行赋给变量 a,依次执行知道记录读取完。 这个程序其实是有问题的,问题就出现在模式中的~符号上,~是匹配的意思, 也就是说,如果当前行是“aa”,而上一行是“aaa”,同样也匹配,这样就不会 打印出“aa”,实际是应该要被打印出来的 # more file aaa aa 234532 3456 555555 -12 32423 -2354235 # awk 'a !~ $0; { a = $0 }' file aaa 234532 3456 555555 -12 32423 -2354235 可以看到,记录“aa”,被程序认为和“aaa”一样而不被打印出来。 要解决这个问题也不是没有办法,请看 # more file aaa aa 234532 3456 555555 234532 3456 555555 -12 32423 -2354235 # awk 'a != $0; { a = $0 }' file aaa aa 234532 3456 555555 -12 32423 -2354235 这里比较的是整个记录是否相同,因此不会出现正则表达式错误匹配的问题。 43、删除多余相同的行(不连续的也删除) #more file foo bar foo baz # awk '!a[$0]++' file foo bar baz 这个程序非常常用。注意到这里使用了关联数组 a,同时测试当前行在之前是否 出现过,如果之前出现过相同的行,那么 a[$0]>0,!a[$0]等于 0,即模式不匹配, 默认的{print}也就不执行,反之如果之前没有出现过该行,则打印出来。 我们来看一下执行过程。读取了第一行后,awk 开始查看模式!a[foo]++,因为 foo 第一次出现,因此 a[foo]=0 为假,但是!a[foo]为真,因此 awk 打印出“foo”, 然后通过++,a[foo]增加了 1,这个数组 a 就有了第一个元素 a[foo]=1. 同样,读取第二行的时候 a[bar]也为假,同样打印出“bar”,同时 a[bar]增长为 a[bar]=1。 第三行,foo 第二次出现,因为这时候 a[foo]=1 为真,所以模式!a[foo]为假,不 匹配模式,不执行动作。a[foo]又增加了 1 变成了 a[foo]=2,这样数组 a 中就有了 两个元素 a[foo]=2,a[bar]=1。 同理,最后一行也打印出来。 这里有另一种方法可以得到相同的效果,据说这是效率最高的方法 # awk '!($0 in a) { a[$0]; print }' file 这个跟之前的命令差不了太多,不同的是使用了 in 操作符,这个程序,同样判 断当前记录是否已经存在。存在则不打印,反之打印。 44、把每 5 行记录用逗号连起来 #more file 1 2 3 4 5 6 7 #awk 'ORS=NR%5?",":"\n"' file 1,2,3,4,5 6,7, ORS 我们知道是输出的记录分隔符,输出记录分割符附加在每行记录的结尾,在 这个程序中,平时使用的换行符是逗号,当行数是 5 的倍数的时候,把 ORS 换 成换行符“\n”,依次类推。请注意,这里的输出虽然只有两行,但是实际的记 录数还是 7 个。 PS:awk 中的 ORS=NR%5?",":"\n"是从 c 语言中借鉴过来的,实际上是一个赋值语 句,是这样运行的,前面提到过 NR 是行号。当 NR%5 为真,也就是 NR%5 不等 于 0(NR 的值不是 5 的倍数)时,ORS 取值为“,”也就是冒号前的字符,反之 当 NR 的值是 5 的倍数的时候,ORS 取值为“\n”,也就是换行了。 第三部分: 有选择的打印行 45、打印文件的前 5 行(相当于 head -5) #more file a b c d e f g h i j k l m n o p q r s t u v w x y z # awk 'NR < 6' file a b c d e f g h i j k l m n o 前面也提到过,NR 的意思是只当前文件的行号,每读一行,awk 会自动给 NR 增加 1。这个命令只有一个模式 NR <6,而没有{动作},默认为{print $0}。模式中 指的是匹配行号 NR<6(即第一到第五行),即把文件 file 的第一到第五行打印出来。 从第六行开始,awk 检测到 NR>=6,因此不执行动作{print}。 这个程序的效率不高,因为当 NR>=6 时,awk 会继续遍历文件直到结束,但其实 后面做的都是无用功,因为不执行任何动作。以下是改进的程序 # awk '1; NR == 5 { exit }' file a b c d e f g h i j k l m n o 语句 NR==5{exit}能够确保文件遍历到第五行的时候立刻停止程序。当文件的 NR 小于 5 的时候,语句“1”相当于{print},因此当 NR<=5 的时候,awk 打印出所 有匹配的行,然后程序停止。当文件的行数非常多的时候,提高执行的效率。 46、打印文件的第一行(head -1) # awk 'NR > 1 { exit }; 1' file a b c 和前面的例子一样,只有当 NR==1 的时候模式才匹配,读第一行的时候,模式 不匹配,直接到第二个语句“1”,也就是{print},打印第一行,读到第二行的时 候 NR=2,匹配模式,执行{exit},退出了 awk,完毕。 47、打印文件的倒数 2 行(tail -2) # awk '{ y=x "\n" $0; x=$0 }; END { print y }' file v w x y z 这个 awk 程序时怎么样工作的呢,首先看第一个语句{ y=x "\n" $0; x=$0 },这个 语句只有动作没有模式,因此对所有行都执行动作。读取第一行的时候,变量 y 被赋值为”\nline1”(因为 x 没有被定义),然后定义变量 x 为”line1”. 读取第二 行的时候,变量 y 被赋值为”line1\nline2”,变量 x=line2。依次类推,读取第三 行的时候 y=”line2\nline3”,x=”line3”,一直到读取最后一行的时候,y=” lineN-1\nlineN”,x=”lineN”。这样变量 y 就包含了最后两行的内容,通过 END{print y}将其打印出来。 仔细想想这个程序,大家能够看得出来这个程序的效率也是非常低的。Awk 程序 读取了整个文件的所有行,但是最后的动作无非是打印出来最后两行。不行的是 在 awk 中没有像 seek()这样的函数,因此无法在文件中找出最后两行(用 tail 可以找出),因此如果要打印出文件最的最后几行,还是把这个任务交给 tail 吧。 48、打印文件的最后一行(tail -1)
/
本文档为【Awk one-liners explained 中文版】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索