【问题标题】:Swap two strings in the same line starting with a specific character交换以特定字符开头的同一行中的两个字符串
【发布时间】:2019-12-01 19:14:14
【问题描述】:

我正在处理一个包含多行的文件,我需要交换其中的一些字符串块。

假设我有这种结构:

# some comment
here is some text
f  483/1318/1298 484/1319/1299 485/1320/1300
f  486/1321/1301 485/1320/1300 484/1319/1299
f  64//59 67//62 76//71
v 1 2 3

我只想交换以f 字符开头的行的最后两个字符串块(注意 f 后面有 2 个空格)。

所以我想得到的结果是:

# some comment
here is some text
f  483/1318/1298 485/1320/1300 484/1319/1299
f  486/1321/1301 484/1319/1299 485/1320/1300
f  64//59 76//71 67//62 
v 1 2 3

我应该使用什么 sed 命令(或任何其他命令)?

【问题讨论】:

    标签: bash macos awk sed


    【解决方案1】:

    awk 可以很简单地做到这一点 - awk '$0~/^f/ {x=$4;$4=$3;$3=x;} 1' file

    $0 ~ /^f/ 仅对以f 开头的行执行以下块。 $0 完全是当前行。

    {x=$4;$4=$3;$3=x;} 仅对匹配的行执行并将第 4 个字段(由空格分隔)存储在变量中,然后将其设置为第 3 个字段,然后将第 3 个字段设置为变量。

    1 有 awk 打印所有行,因为 awk 的默认行为是 print 并且一个是真实的。

    $ cat file
    # some comment
    here is some text
    f  483/1318/1298 484/1319/1299 485/1320/1300
    f  486/1321/1301 485/1320/1300 484/1319/1299
    f  64//59 67//62 76//71
    v 1 2 3
    
    $ awk '$0~/^f/ {x=$4;$4=$3;$3=x;} 1' file
    # some comment
    here is some text
    f 483/1318/1298 485/1320/1300 484/1319/1299
    f 486/1321/1301 484/1319/1299 485/1320/1300
    f 64//59 76//71 67//62
    v 1 2 3
    

    【讨论】:

    • 您可以通过将 $0~/^f/ 更改为 /^f/ 来缩短它,因为它也会测试整条线路。
    • 这会将空格标准化,因此最初的两个空格现在减少为一个。不确定这是否是受欢迎的副作用。
    • 有没有办法保留 2 个空格?还有,原地怎么改?
    【解决方案2】:

    这是sed 解决方案:

    sed '/^f  /s/ \([^ ]*\)  *\([^ ]*\) *$/ \2 \1/' input
    

    这会修改以“f”和两个空格开头的任何行,交换最后两个字段,其中字段由空格分隔。

    【讨论】:

      【解决方案3】:

      gawk 的第 4 版或更高版本中,它为 split 提供第四个参数:

      awk '$1 == "f" {fieldcount=split($0, fields, / +/, seps); x=fields[3]; fields[3]=fields[4]; fields[4]=x; printf "%s", seps[0]; for (i=1; i<=fieldcount; i++) {printf "%s%s", fields[i], seps[i]}; printf "\n"} $1 != "f" {print}'
      

      为了可读性而分开:

      awk '$1 == "f" {
              fieldcount = split($0, fields, / +/, seps); 
              x = fields[3]; 
              fields[3] = fields[4]; 
              fields[4] = x; 
              printf "%s", seps[0]; 
              for (i=1; i<=fieldcount; i++) {
                  printf "%s%s", fields[i], seps[i]
              }; 
              printf "\n"
          } 
          $1 != "f" {print}'
      

      这会保存字段分隔符,以便它们可以按原样输出。

      【讨论】:

      • 返回错误:awk: syntax error at source line 1 context is $1 == "f" {fieldcount=split($0, fields, / &gt;&gt;&gt; +/, &lt;&lt;&lt; awk: illegal statement at source line 1 awk: illegal statement at source line 1
      • @Blueko:你使用的是什么版本的 awk?如果不是 gawk 4 或更高版本,则无法正常工作。
      • @Blueko:那不是“gawk 的第 4 版或更高版本”,所以我的回答对你不起作用。
      【解决方案4】:

      这可能对你有用(GNU sed):

      sed -E 's/^(f  .*) (\S+) (\S+) *$/\1 \3 \2/' file
      

      使用模式匹配、分组和反向引用。

      【讨论】:

      • @Blueko 也许行尾有空格。查看更新。
      猜你喜欢
      • 1970-01-01
      • 2017-09-20
      • 2017-04-18
      • 2020-12-25
      • 2021-03-22
      • 2022-11-28
      • 2017-08-08
      • 1970-01-01
      • 2020-05-11
      相关资源
      最近更新 更多