Awk交换来自不同列的值

问题描述 投票:0回答:2

我有一个以下结构的文件:

4 1:4 3:1 6:1 56:2 57:4 58:7 59:1 66:2
1 2:1 56:1 57:1 58:2 59:2 65:1
8 1:2 2:1 3:8 12:1 56:1 57:2 58:2 59:2 66:1 67:2

我需要做的是将与56和57相关的值切换为与58和59相关的值:

4 1:4 3:1 6:1 56:7 57:1 58:2 59:4 66:2
1 2:1 56:2 57:2 58:1 59:1 65:1
8 1:2 2:1 3:8 12:1 56:2 57:2 58:1 59:2 66:1 67:2

目前我正在尝试使用以下内容执行至少两列的替换:

awk '{
         for ( i=2;i<=NF;i++ )
         {
            split(, a, ":") 
            arr[a[1]] = a[2]
         }
      }
      END {
         n = asorti(arr, dest)
         ORS= 
         for ( i=1; i<=n; i++ ) 
         { 
            if ( dest[i] != 56 && dest[i] != 58 ) 
               print dest[i] ":" arr[dest[i]]
            else 
            { 
               if ( dest[i] == 56 ) 
                  print dest[i] ":" arr[dest[i+2]] 
               if ( dest[i] == 58 ) 
                  print dest[i] ":" arr[dest[i-2]]
            }
         }
      }' file

但是,这看起来很笨重,最终索引没有正确排序。将欣赏任何其他解决方案。

arrays bash awk
2个回答
1
投票

如果你可以依赖订购总是56,57,58,59,那么你可以用sed做到这一点:

sed -E 's/(56:)([0-9]+)(.*57:)([0-9]+)(.*58:)([0-9]+)(.*59:)([0-9]+)/\1\6\3\8\5\2\7\4/' file

只需捕获所有零件并在更换中重新洗牌。

奇数编号的捕获组指的是“标签”和任何主要内容,除了在第一种情况下我们不必触摸该部分线路。偶数编号指的是数值。


1
投票

GNU awk解决方案:

awk '{ 
         r=gensub(/\<(56:)([0-9]+) (57:)([0-9]+) (58:)([0-9]+) (59:)([0-9]+)/, 
                 "\\1\\6 \\3\\8 \\5\\2 \\7\\4", "g"); 
         print r 
     }' file

输出:

4 1:4 3:1 6:1 56:7 57:1 58:2 59:4 66:2
1 2:1 56:2 57:2 58:1 59:1 65:1
8 1:2 2:1 3:8 12:1 56:2 57:2 58:1 59:2 66:1 67:2

gensub(regexp, replacement, how [, target])

在目标字符串target中搜索正则表达式regexp的匹配项。如果how是以'g'或'G'(“global”的缩写)开头的字符串,则将regexp的所有匹配替换为replacement

© www.soinside.com 2019 - 2024. All rights reserved.