【问题标题】:Replace a column in file with a column from a different file while retaining the format将文件中的列替换为其他文件中的列,同时保留格式
【发布时间】:2021-12-13 23:36:38
【问题描述】:

我遇到了一个对于高级 shell 用户来说似乎不太难的问题。这就是问题所在。

我有 2 个文件:

File1 格式如下:

ALPH      1  M   GIF M   1      11.111  23.123  -4.412  1.00  0.00           A
ALPH      2  BA  GIF M   1      22.222  78.251  -6.215  2.00  0.00           B
ALPH      3  C   GIF M   1      22.223  46.321  -6.124  3.00  0.00           C
ALPH      4  D   GIF M   1      23.333  15.214  -6.125  4.00  0.00           D
ALPH      5  AB  GIF M   1      24.111  61.458  -8.214  5.00  0.00           E
ALPH      6  LM  GIF M   1      25.333  78.214  -9.321  6.00  0.00           F
ALPH      7  BA  GIF M   1      17.645  87.256  -9.365  7.00  0.00           G
ALPH      8  BA2 GIF M   1      14.125  19.365  -1.258  8.00  0.00           H
-
-

单列文件2:

-0.14
-0.47
-0.58
-0.69
-0.25
-0.69
-0.12
-0.44

我想将 File1 中的第 11 列替换为 File2 中的唯一列。我执行以下操作

paste File1 File2 | awk '{$11=$13;$13=""}1' > output

虽然它很好地替换了列,但它弄乱了我想保留的 File1 的原始格式。如您所见,File1 的所有字段之间存在不同数量的空格,即使在替换 $11 之后,我也希望保留它。

我尝试了几种方法,包括columnprintf,但似乎都没有奏效。也许我做错了什么。

有谁知道我如何最好使用 awk 或 sed 达到预期的效果?

谢谢!

罗希特

【问题讨论】:

    标签: bash sed awk


    【解决方案1】:

    如果您需要保留固定宽度的列,您可以使用子字符串:

    cat file1
    echo
    awk '
        NR==FNR {v[FNR]=$1; next}
        {print substr($0,1,62) sprintf("%-15s", v[FNR]) substr($0,78)}
    ' file2 file1
    
    ALPH      1  M   GIF M   1      11.111  23.123  -4.412  1.00  0.00           A
    ALPH      2  BA  GIF M   1      22.222  78.251  -6.215  2.00  0.00           B
    ALPH      3  C   GIF M   1      22.223  46.321  -6.124  3.00  0.00           C
    ALPH      4  D   GIF M   1      23.333  15.214  -6.125  4.00  0.00           D
    ALPH      5  AB  GIF M   1      24.111  61.458  -8.214  5.00  0.00           E
    ALPH      6  LM  GIF M   1      25.333  78.214  -9.321  6.00  0.00           F
    ALPH      7  BA  GIF M   1      17.645  87.256  -9.365  7.00  0.00           G
    ALPH      8  BA2 GIF M   1      14.125  19.365  -1.258  8.00  0.00           H
    
    ALPH      1  M   GIF M   1      11.111  23.123  -4.412  1.00  -0.14          A
    ALPH      2  BA  GIF M   1      22.222  78.251  -6.215  2.00  -0.47          B
    ALPH      3  C   GIF M   1      22.223  46.321  -6.124  3.00  -0.58          C
    ALPH      4  D   GIF M   1      23.333  15.214  -6.125  4.00  -0.69          D
    ALPH      5  AB  GIF M   1      24.111  61.458  -8.214  5.00  -0.25          E
    ALPH      6  LM  GIF M   1      25.333  78.214  -9.321  6.00  -0.69          F
    ALPH      7  BA  GIF M   1      17.645  87.256  -9.365  7.00  -0.12          G
    ALPH      8  BA2 GIF M   1      14.125  19.365  -1.258  8.00  -0.44          H
    

    【讨论】:

      【解决方案2】:

      当您在 awk 中为字段分配值时,它会使用 OFS 的当前值重新编译当前记录以分隔字段。因此,要保留原始间距,您不能为字段分配新值。相反,您必须使用 RE 来描述在分配之前和之后要跳过多少个非空格/空格。像这样使用 GNU awk 将字母“c”(第三个字段,因此下面的数字“2”表示要跳过的前导字段的数量)替换为单词“BOB”:

      $ echo "a   b c    d e" |
      gawk '{print gensub(/(([^[:space:]]+[[:space:]]+){2})[^[:space:]]+/,"\\1BOB","")}'
      a   b BOB    d e
      

      这会保留间距,因为您正在处理整条记录,而不仅仅是一个字段,因此 awk 不会重新编译记录。

      所以你的情况是:

      $ cat file1
      ALPH      1  M   GIF M   1      11.111  23.123  -4.412  1.00  0.00           A
      ALPH      2  BA  GIF M   1      22.222  78.251  -6.215  2.00  0.00           B
      ALPH      3  C   GIF M   1      22.223  46.321  -6.124  3.00  0.00           C
      ALPH      4  D   GIF M   1      23.333  15.214  -6.125  4.00  0.00           D
      ALPH      5  AB  GIF M   1      24.111  61.458  -8.214  5.00  0.00           E
      ALPH      6  LM  GIF M   1      25.333  78.214  -9.321  6.00  0.00           F
      ALPH      7  BA  GIF M   1      17.645  87.256  -9.365  7.00  0.00           G
      ALPH      8  BA2 GIF M   1      14.125  19.365  -1.258  8.00  0.00           H
      $          
      $ cat file2
      -0.14
      -0.47
      -0.58
      -0.69
      -0.25
      -0.69
      -0.12
      -0.44
      $ 
      $ gawk 'NR==FNR{map[FNR]=$0; next} {print gensub(/(([^[:space:]]+[[:space:]]+){10})[^[:space:]]+/,"\\1" map[FNR],"")}' file2 file1
      ALPH      1  M   GIF M   1      11.111  23.123  -4.412  1.00  -0.14           A
      ALPH      2  BA  GIF M   1      22.222  78.251  -6.215  2.00  -0.47           B
      ALPH      3  C   GIF M   1      22.223  46.321  -6.124  3.00  -0.58           C
      ALPH      4  D   GIF M   1      23.333  15.214  -6.125  4.00  -0.69           D
      ALPH      5  AB  GIF M   1      24.111  61.458  -8.214  5.00  -0.25           E
      ALPH      6  LM  GIF M   1      25.333  78.214  -9.321  6.00  -0.69           F
      ALPH      7  BA  GIF M   1      17.645  87.256  -9.365  7.00  -0.12           G
      ALPH      8  BA2 GIF M   1      14.125  19.365  -1.258  8.00  -0.44           H
      

      如果你没有 gawk(用于 gensub()),你可以使用 match() 来查找你关心的字段的开始位置,第二个 match() 来查找它的结束位置,以及明智的 substr() 来查找用新值替换它。

      @GlennJackman 在他的解决方案中提到了固定宽度字段。如果这就是你所拥有的,你可以使用 GNU awks FIELDWIDTHS 变量来指定每个字段的宽度并使用它。有关详细信息,请参阅 gawk 手册。

      【讨论】:

        【解决方案3】:

        使用awk

        awk 'FNR==NR {a[NR]=$0;next} {$11=a[FNR]}1' OFS="\t" a t
        ALPH    1       M       GIF     M       1       11.111  23.123  -4.412  1.00    -0.14   A
        ALPH    2       BA      GIF     M       1       22.222  78.251  -6.215  2.00    -0.47   B
        ALPH    3       C       GIF     M       1       22.223  46.321  -6.124  3.00    -0.58   C
        ALPH    4       D       GIF     M       1       23.333  15.214  -6.125  4.00    -0.69   D
        ALPH    5       AB      GIF     M       1       24.111  61.458  -8.214  5.00    -0.25   E
        ALPH    6       LM      GIF     M       1       25.333  78.214  -9.321  6.00    -0.69   F
        ALPH    7       BA      GIF     M       1       17.645  87.256  -9.365  7.00    -0.12   G
        ALPH    8       BA2     GIF     M       1       14.125  19.365  -1.258  8.00    -0.44   H
        

        由于sub 的错误,编辑已恢复为原始版本

        【讨论】:

          【解决方案4】:

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

          sed = file2 | sed -r '$!N;s|(.*)\n(.*)|\1s/\\S+/\2/11|' | sed -rf - file1
          

          【讨论】:

            【解决方案5】:

            纯 awk 解决方案:

            awk 'FNR==NR{a[NR]=$0;next} {sub($11, a[FNR])}1' file2 file1
            ALPH      1  M   GIF M   1      11.111  23.123  -4.412  1.00  -0.14           A
            ALPH      2  BA  GIF M   1      22.222  78.251  -6.215  2.00  -0.47           B
            ALPH      3  C   GIF M   1      22.223  46.321  -6.124  3.00  -0.58           C
            ALPH      4  D   GIF M   1      23.333  15.214  -6.125  4.00  -0.69           D
            ALPH      5  AB  GIF M   1      24.111  61.458  -8.214  5.00  -0.25           E
            ALPH      6  LM  GIF M   1      25.333  78.214  -9.321  6.00  -0.69           F
            ALPH      7  BA  GIF M   1      17.645  87.256  -9.365  7.00  -0.12           G
            ALPH      8  BA2 GIF M   1      14.125  19.365  -1.258  8.00  -0.44           H
            

            【讨论】:

              【解决方案6】:

              或者使用剪切:

              cat file1 | cut -c 1-61 > section1
              cat file1 | cut -c 67-80 > section2
              paste section1 file2 section2 -d '\0' > output
              

              文件格式看起来像 pdb 文件,字段之间的空格不相关,重要的是字段宽度

              【讨论】:

                猜你喜欢
                • 2019-09-24
                • 1970-01-01
                • 2014-11-18
                • 1970-01-01
                • 2020-09-25
                • 1970-01-01
                • 2014-06-07
                • 2015-01-17
                • 2021-03-24
                相关资源
                最近更新 更多