AWK只打印某个域后的所有域

时间:2022-03-13 14:36:48
如转载请指明(博客http: yangzhigang cublog cn)。前言:有时我们需要将某个域之后的所有域打印出来,而且每个记录(行)的域的个数也不一定,所以用$4,$5,… $n,… $(NF-1),$NF穷举是不现时的,
如转载请指明“(源自: 博客 http://yangzhigang.cublog.cn)”。
 
前言:
有时我们需要将某个域之后的所有域打印出来,而且每个记录(行)的域的个数也不一定,所以用“$4,$5,…..$n,….$(NF-1),$NF”穷举是不现时的,我经过测试,总结了一下实现的方法,供大家参考。
 
一.利用输出函数printf
// 测试文件内容
[root@cacti tmp]# cat file.txt 
x1 x2 x3
x1 x2 x3 x4 x5
x1 x2 x3 x4 x5 x6
x1 x2 x3 x4 x5 x6 x7 x8
x1 x2 x3 x4 x5 x6 x7
 
[root@cacti tmp]# awk '{for(i=4;i<=NF;i++) printf"%s ",$i} {print ""}' file.txt
 
x4 x5 
x4 x5 x6 
x4 x5 x6 x7 x8 
x4 x5 x6 x7
[root@cacti tmp]# awk '{for (i=4;i<=NF;i++) {printf $i" "}printf "\n"}' file.txt
 
x4 x5 
x4 x5 x6 
x4 x5 x6 x7 x8 
x4 x5 x6 x7
 
问题:
1) 在NF不够4个的记录(行),将会打印出一个空行;
2) 在输出的结果中,每行结尾多了一个空格;
 
下面两种命令,分别对上面两个命令加了NF>4的判断,结果是一致的,解决了上面的第一个问题:“在NF不够4个的记录(行),将会打印出一个空行;”。
[root@cacti tmp]# awk '{for(i=4;i<=NF;i++) printf"%s ",$i};NF>4 {print ""}' file.txt 
x4 x5 
x4 x5 x6 
x4 x5 x6 x7 x8 
x4 x5 x6 x7
[root@cacti tmp]# awk 'NF>4 {for (i=4;i<=NF;i++) {printf $i" "}printf "\n"}' file.txt 
x4 x5 
x4 x5 x6 
x4 x5 x6 x7 x8 
x4 x5 x6 x7
 
二.利用字符串函数index和substr
// 测试文件内容
[root@cacti tmp]# cat file.txt 
x1 x2 x3
x1 x2 x3 x4 x5
x1 x2 x3 x4 x5 x6
x1 x2 x3 x4 x5 x6 x7 x8
x1 x2 x3 x4 x5 x6 x7
[root@cacti tmp]# cat file.txt |awk '{a=index($0,$4);print substr($0,a)}'
x1 x2 x3
x4 x5
x4 x5 x6
x4 x5 x6 x7 x8
x4 x5 x6 x7
 
可以发现,执行上面这个命令时,在NF不够4个的记录(行),将会打印出整行记录;为解决这个问题,可以像上文所采用的方法,加上“NF>4”,如下:
[root@cacti tmp]# cat file.txt |awk 'NF>4 {a=index($0,$4);print substr($0,a)}'
x4 x5
x4 x5 x6
x4 x5 x6 x7 x8
x4 x5 x6 x7
 
这种方法似乎很完美,但是分析实现原理,可以发现,本方法是通过分析出$4这个字段的字串在$0(整个记录)中第一次出现的位置,记数为a,之后再截取a之后的字串,并打印。那么,如果$4的字串在之前就出次过,则a的数值就是前面的出现的位置了,结果就会是错误的,为解决这个问题,可以将$4替换一下,再定位,测试如下:
// 测试文件内容
[root@cacti tmp]# cat filexx.txt 
x1 x2 x3
x1 x4 x3 x4 x5
x1 x4 x4 x4 x5 x6
x4 x2 x3 x4 x5 x6 x7 x8
x1 x2 x3 x4 x4 x6 x7
(本文件,可以发现$4的字串:x4,在2,3,4,5行,在$4之前的域都有出现过与之相同的字串:x4)
//下面是用上面的方法,结果显然有误的,是将以x4开始的域之后的所有域全打印出来了。
[root@cacti tmp]# cat filexx.txt |awk 'NF>4 {a=index($0,$4);print substr($0,a)}'
x4 x3 x4 x5
x4 x4 x4 x5 x6
x4 x2 x3 x4 x5 x6 x7 x8
x4 x4 x6 x7
 
//对$4重新赋值,在$4前加个“z”,以区分之前域于之相同的字串
[root@cacti tmp]# cat filexx.txt |awk 'NF>4 {$4="z"$4;a=index($0,$4);print substr($0,a)}'
zx4 x5
zx4 x5 x6
zx4 x5 x6 x7 x8
zx4 x4 x6 x7
 
//将上个命令中加的字串“z”过滤掉,得到想要的结果
[root@cacti tmp]# cat filexx.txt |awk 'NF>4 {$4="n"$4;a=index($0,$4);print substr($0,a+1)}'
x4 x5
x4 x5 x6
x4 x5 x6 x7 x8
x4 x4 x6 x7
 
//指定分隔符为一个空格,指定(多个)分隔符时,要写在方括号中,此方法对某个字段(如yang  zhi  gang为表示名字的字段)中有多个空格很有用。
[root@cacti tmp]# cat filexx.txt |awk 'BIGIN{FS="[ ]"} NF>4 {$4="n"$4;a=index($0,$4);print substr($0,a+1)}'
x4 x5
x4 x5 x6
x4 x5 x6 x7 x8
x4 x4 x6 x7
 
三.利用域值替换
    就是将某个(些)域替换成空值,如去除第一个域为:awk '{ $1=""; print $0 }' file.in
// 测试文件内容
[root@cacti tmp]# cat file.txt 
x1 x2 x3
x1 x2 x3 x4 x5
x1 x2 x3 x4 x5 x6
x1 x2 x3 x4 x5 x6 x7 x8
x1 x2 x3 x4 x5 x6 x7
 
//前3个域用字母a替换
[root@cacti tmp]# awk '{ for(i=1;i<=3;i++){$i="a"}; print $0 }' file.txt 
a a a
a a a x4 x5
a a a x4 x5 x6
a a a x4 x5 x6 x7 x8
a a a x4 x5 x6 x7
 
//前3个域用空格替换
[root@cacti tmp]# awk '{ for(i=1;i<=3;i++){$i=""}; print $0 }' file.txt 
  
   x4 x5
   x4 x5 x6
   x4 x5 x6 x7 x8
   x4 x5 x6 x7
问题:
1) 在NF不够4个的记录(行),将会打印出一个空行;
2) 在输出的结果中,去除的域会用空格来代替
 
// 去除上面命令输出的空行,但输出结果前端会有空格
[root@cacti tmp]# awk 'NF>4 { for(i=1;i<=3;i++){$i=""}; print $0 }' file.txt
   x4 x5
   x4 x5 x6
   x4 x5 x6 x7 x8
   x4 x5 x6 x7
 
总结:
   本文介绍了三种方法来解决AWK“只打印第N个域之后的所有域”的问题。
   第一和第三种方法会出现输出结果后端或前端加空格的情况,但这可能不会影响你后绪的操作,可以结合管道再处理。
   根据你的需要选择合适的方法吧。