【问题标题】:Change column order (different number of columns)更改列顺序(不同的列数)
【发布时间】:2017-11-07 10:14:11
【问题描述】:

原始文件如下所示:

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 可以做到这一点,但不知道该怎么做。

【问题讨论】:

  • 您尝试过什么吗?假设您知道awk 可以做到这一点?
  • @EdMorton 只是将 on_2 设置为最后一个值/列(如果存在)并保留与其他列相同的顺序。
  • edit 你的问题是要说明这一点以及任何其他迄今为止未说明的要求,包括“on_2”字段是否总是出现在同一个位置(如果它没有然后更新你的例子) .
  • on_2 field always occurs in the same spot - after ind and before spd 好的,ind 和 spd 总是出现在同一个位置吗?我试图让您简单地告诉我们 on_2 是否始终是第 6 个字段,或者我们是否必须搜索 ind 然后 on_2 将在之后搜索,无论它出现在哪里。
  • 是的,如果存在,on_2 始终是第 6 个字段。

标签: bash awk


【解决方案1】:

简单地说:如果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=[^ ]* /) - 在记录/行/行中搜索正则表达式 (on2=[^ ]*)

    /on_2=[^ ]* /

    • on_2= 与字符 on_2= 逐字匹配(区分大小写)
    • 匹配下面列表中不存在的单个字符[^ ]*
    • * Quantifier — 匹配零次到无限次,尽可能多次,按需回馈(贪婪)
    • 按字面意思匹配字符(区分大小写)
  • 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】:

    所以您想在匹配时将第 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

    【讨论】:

    • 没有提供 OP 想要的输出。
    • @glenn jackman 谢谢,实际上这不是 OP 要求的交换。我在轮班时更新了答案,这样就可以了,尽管现在看起来效率不高。
    【解决方案3】:
    $ 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&lt;blank&gt;$6&lt;blank&gt;bar 更改为 foo&lt;blank&gt;&lt;blank&gt;bar,即在 $6 现在为空且字段数不变且 $5 仍为 foo 且 $7 仍为 bar 的位置之前和之后保留空白。

    然后使用 $0=$0 导致 awk 将 $0 重新拆分为字段,因此 foo 和 bar 之间的多个空格被视为单个字段分隔符,因此尽管 $5 仍然是 foo,现在 $6 是 bar 并且有 1少场就行。 $0 仍然在 foo 和 bar 之间包含 2 个空格,尽管这并没有改变 $0,它只是改变了它被拆分为各个字段分配的方式。

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

    因此 $0=$0 为 $1->$NF 创建新值,并且 $1=$1 导致它们之间的所有 FS 都被 OFS 替换。

    【讨论】:

    • 能否请您补充一下为什么$0=$0; $1=$1 修复了对齐方式,谢谢。
    【解决方案4】:

    另一个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
    

    【讨论】:

    • -r 仅适用于 gnu,请改用 -E,因为它适用于 gnu 和 osx seds。
    【解决方案5】:

    您能否尝试关注并告诉我这是否对您有帮助(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
    

    【讨论】:

    • sed 不必匹配on_2 之前的前缀。它会稍微缩小代码。
    • 好主意,但[^(on_2)] 并没有按照您的想法做:它不匹配“not 'on_2'”——它匹配任何不是(o 的字符或n_2)。你想要sed -r 's/( on_2=[^ ]+)(.*)/\2\1/' file
    • -r 仅适用于 gnu,请改用 -E,因为它适用于 gnu 和 osx seds。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多