输出包含特定字符的列

公司的一个同事在写shell 的时候,提了这么一个需求,sar的输出中,将idle列输出 ,而且要求适用不同的平台 。在linux下通过awk打印$NF列就是ilde列,假设在其他平台下,如freebsd下idle列是$(NF-1)列,hp-unix下是$1列 (只是假设,请勿直接对号入座),该shell 该怎么写?

思路:先是想到的通过awk找到匹配idle字符串的列号,再打印该列即可。不过琢磨了一下午没实现,后来又想到通过将列转换为行,再grep idle行即可。后种思路通过测试实现了。

一、python实现(列转行思路)

这个是最快写出的一种方法,原理是列转行,再判断某个关键字在不在行里(即转换前的列里),在就打印出来。

测试文件如下:

[root@361way ~]# cat /tmp/test.file
CPU     %user     %nice   %system   %iowait    %steal     %idle
all      0.50      0.00      0.50      0.00      0.00     98.99
all      0.50      0.00      0.00      0.50      0.00     99.00
all      0.50      0.00      0.25      0.25      0.00     99.00

python代码如下:

with open('test.file') as f:
  lis = [x.split() for x in f]
for x in zip(*lis):
    if '%idle' in x:
        print x

先是将各行取出,并通过空格切分为各个元素,lis的结果为[(a,b) ,(c,d)] 这样的格式。再通过zip组合为列。最后就是判断了。很简单,对吧!

二、awk+grep实现(列转行)

awk实现行列转换的代码如下:

# awk '{for(i=1;i<=NF;i++)a[NR,i]=$i}END{for(j=1;j<=NF;j++)for(k=1;k<=NR;k++)printf k==NR?a[k,j] RS:a[k,j] FS}' test.file

看起来是不是很蛋疼 ,连三元运算符都用上了。简化下:

[root@361way tmp]# awk '{for(i=0;++i<=NF;)a[i]=a[i]?a[i] FS $i:$i}END{for(i=0;i++<NF;)print a[i]}'  test.file
CPU all all all
%user 0.50 0.50 0.50
%nice 0.00 0.00 0.00
%system 0.50 0.00 0.25
%iowait 0.00 0.50 0.25
%steal 0.00 0.00 0.00
%idle 98.99 99.00 99.00

最后实现的代码为:

awk '{for(i=0;++i<=NF;)a[i]=a[i]?a[i] FS $i:$i}END{for(i=0;i++<NF;)print a[i]}'  test.file |grep idle

貌似看起来还不错,不过感觉还是不满意,还是想通过直接列匹配实现。

三、直接列匹配

把问题在群里抛出后,果然有大牛搞出了直接awk匹配idle字符串的列号,再打印该列的方法。具体如下:

方法1:for循环

[root@361way tmp]# awk 'NR==1{for(i=1;i<=NF;i++)if($i~/idle/)n=i}NR>1{print $n}' test.file
98.99
99.00
99.00

方法2:when循环 , array取

[root@361way tmp]# awk 'NR==1{while(n++<NF)a[$n]=n} {if( NR>1) print $a["%idle"]}' test.file
98.99
99.00
99.00

在数据最比较大时,这里的方法1相对更高效。

方法3:python实现

python在简单文本流处理上体现不出优势,我这里写了一个,代码感觉相当长,但便于理解---原理还是逐行取某列,代码如下:

import linecache
theline = linecache.getline(r'/tmp/sar.txt', 1)
nums = range(len(theline.split()))
titles = theline.split()
for num in nums:
     if 'idle' in titles[num]:
         print num
         with open('/tmp/sar.txt') as f:
              for row in f:
                  print row.split()[num]

最后:这里再推荐一个perl 版的工具csvgrep ,具体可以参该工具示例中" csvgrep '@header $NR == 1; ${Name}' contacts.csv " 的用法 。实际应用中也有将某行转为列的需求,或者将某列改为行输出,这样便于取值。这里再给一个方便将某列转为行的代码,如将最后一列转为行:

awk '{printf "%s", $NF}' file 




本站的发展离不开您的资助,金额随意,欢迎来赏!

You can donate through PayPal.
My paypal id: itybku@139.com
Paypal page: https://www.paypal.me/361way

分类: perl/php/python/gawk/sed 标签:
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.