更改列顺序(不同列数)

时间:2017-11-06 13:38:12

标签: bash awk

原始文件如下所示:

stat.sn 15094 291 usf=630 ind=32615 on_2=ON-14-6003 spd=307 i_pow=150
stat.sn 15094 361 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 360 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 272 usf=630 ind=32615 on_2=ON-14-6003 spd=307 i_pow=150
stat.sn 15094 276 usf=630 ind=32615 on_2=ON-14-6003 spd=307 i_pow=150

我需要它像这样:

stat.sn 15094 291 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 361 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 360 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 272 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 276 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003

简单地说:如果on_2存在于一行中,它应该是一行中的最后一个值。 其余值的顺序应保持不变。

如果存在,则on_2始终为第6个字段 - 在 ind 之后和 spd 之前。所有字段在每一行中都具有相同的位置。

我想awk可以实现这一点,但无法弄清楚如何做到这一点。

5 个答案:

答案 0 :(得分:2)

所以你想在匹配时最后移动$ 6列。

awk '$6 ~ /^on_2/ { t=$6; $6=$7; $7=$8; $8=t } 1' file

如果匹配模式可能存在于任何列中,我们希望始终在最后打印它。

awk '{ 
    for(i=1;i<NF-1;i++)
        if($i~/^on_2/) {
            t=$i;
            for(j=i;j<NF;j++)
                $j=$(j+1);
            $NF=t
        }
    } 1' file

我们可以通过设置t=$i; $i=""和打印$0 t来省略此转变,但随后会在移动列的位置打印额外的FS,从而打破对齐。

更新:了解如何使用$(NF+1)=i; $i=""并保留对齐方式,请检查此answer

答案 1 :(得分:2)

  

简单地说:如果on_2存在于一行中,它应该是a中的最后一个值   行。其余值的顺序应保持不变。

获得所需输出的更多方法,无论on_2存在于何处,搜索,提取,无效并将其作为最后一列:

$ awk 'match($0,/on_2=[^ ]* /){s=substr($0,RSTART,RLENGTH);sub(s,"");$0=$0 FS s}1' infile
stat.sn 15094 291 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003 
stat.sn 15094 361 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 360 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 272 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003 
stat.sn 15094 276 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003 

<强>解释

awk 'match($0,/on_2=[^ ]* /){                # Search

         s=substr($0,RSTART,RLENGTH);        # if found extract it

         sub(s,"");                          # remove extracted part from row

         $0=$0 FS s                          # set extracted part at the end 

     }1                                      # print line/record
     ' infile
  • match($0,/on_2=[^ ]* /) - 在记录/行/行中搜索regexp(on2=[^ ]*

    /on_2=[^ ]* /

    • on_2=字面匹配字符on_2=(区分大小写)
    • 匹配[^ ]*
    • 下方列表中不存在的单个字符
    • *量词 - 在零和无限次之间匹配,尽可能多次,根据需要回馈(贪婪)
    • 字面匹配字符(区分大小写)
  • s=substr($0,RSTART,RLENGTH);如果匹配则从记录中提取字符串,并将其保存在变量s

  • sub(s,"") - 在s中搜索字符串,在记录/行中,用null替换

  • $0=$0 FS s - 修改记录/行/行

  • }1 - 1最后执行默认操作,即打印当前/记录/行,print $0。要知道awk如何工作,请尝试awk '1' infile,它将打印所有记录/行,而awk '0' infile则不打印任何内容。除零以外的任何数字都是 true ,这会触发默认行为。

答案 2 :(得分:1)

请问您可以尝试关注并告诉我这是否对您有帮助(GNU sed)。

sed -r 's/( on_2=[^ ]+)(.*)/\2\1/'  Input_file

输出如下。

stat.sn 15094 291 usf=630 ind=32615  spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 361 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 360 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 272 usf=630 ind=32615  spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 276 usf=630 ind=32615  spd=307 i_pow=150 on_2=ON-14-6003

如果要将输出作为制表符空格分隔。

sed -r 's/( on_2=[^ ]+)(.*)/\2\1/' Input_file | column -t

答案 3 :(得分:1)

另一个sed

$ sed -r 's/(\son_2=\S+)(.*)/\2\1/' file

stat.sn 15094 291 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 361 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 360 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 272 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 276 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003

答案 4 :(得分:1)

$ awk '$6 ~ /^on_2=/{$(NF+1)=$6; $6=""; $0=$0; $1=$1}1' file
stat.sn 15094 291 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 361 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 360 usf=630 ind=32658 spd=307 i_pow=150
stat.sn 15094 272 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003
stat.sn 15094 276 usf=630 ind=32615 spd=307 i_pow=150 on_2=ON-14-6003

为什么格式化需要$0=$0; $1=$1

$6=""foo<blank>$6<blank>bar更改为foo<blank><blank>bar,即在$ 6现在为空之前和之后留空,且字段数不变,$ 5仍为foo而7美元仍是bar

然后使用$0=$0导致awk将$0重新拆分为字段,因此foo和bar之间的多个空格被视为单个fied分隔符,所以尽管$ 5仍然是foo,现在$ 6是酒吧和那里的线上少了1个。 $ 0仍然包含foo和bar之间的2个空格,但由于这并没有改变$ 0,它只是改变了它被分成单个字段分配的方式。

然后使用$1=$1(或分配给任何字段)会导致awk使用字段之间的OFS值(空白字符)重新编译$ 6,所以在此之后foo和bar之间只有1个空白2。

因此$ 0 = $ 0为$ 1&gt; $ NF创建新值,$ 1 = $ 1会导致它们之间的所有FS被OFS替换。