在 pdb 文件的每 2-3-4 行使用 awk 和 sed 替换文本

Replace text using awk and sed at every 2-3-4 lines for pdb file

我有一个包含大约 200 000 行的 pdb 文本文件。每行看起来像这样:

COMPND
SOURCE    
HETATM    1  CT  100     1     -23.207  17.632  14.543
HETATM    2  CT   99     1     -22.069  18.353  15.280
HETATM    3  OH  101     1     -21.074  18.762  14.358
HETATM    4  F   103     1     -23.816  18.483  13.675
HETATM    5  F   103     1     -24.119  17.162  15.433
HETATM    6  F   103     1     -22.680  16.591  13.841
HETATM    7  HC  104     1     -21.623  17.681  16.014
HETATM    8  HC  104     1     -22.451  19.218  15.823
HETATM    9  HO  102     1     -21.040  18.108  13.673
HETATM   10  CT  100     2      -4.340 -29.478  45.144
HETATM   11  CT   99     2      -3.051 -29.846  44.395
HETATM   12  OH  101     2      -1.968 -29.072  44.880
HETATM   13  F   103     2      -4.217 -29.778  46.464
HETATM   14  F   103     2      -5.396 -30.156  44.621
HETATM   15  F   103     2      -4.551 -28.140  45.015
HETATM   16  HC  104     2      -3.178 -29.656  43.329
HETATM   17  HC  104     2      -2.829 -30.908  44.511
HETATM   18  HO  102     2      -2.315 -28.222  45.119
HETATM   19  CT  100     3     -49.455 -17.542 -31.718
HETATM   20  CT   99     3     -49.981 -18.984 -31.736
HETATM   21  OH  101     3     -48.905 -19.897 -31.607
HETATM   22  F   103     3     -48.867 -17.273 -30.521
HETATM   23  F   103     3     -50.474 -16.668 -31.929
HETATM   24  F   103     3     -48.527 -17.405 -32.704
...

我必须将 C1 的所有第一个 CT 和 C2 的第二个 CT 更改为 H1、H2 的 F1、F2、F3 和 HC。

是否可以在小脚本中使用 awk 和 sed 更改它们?每个 C1-C2 和 F1、F2、F3 都是同一分子(三氟乙醇 - TFE)的一部分,但有许多 TFE 分子需要定义。

所以我希望它看起来像这样:

COMPND
SOURCE    
HETATM    1  C1  100     1     -23.207  17.632  14.543
HETATM    2  C2   99     1     -22.069  18.353  15.280
HETATM    3  OH  101     1     -21.074  18.762  14.358
HETATM    4  F1  103     1     -23.816  18.483  13.675
HETATM    5  F2  103     1     -24.119  17.162  15.433
HETATM    6  F3  103     1     -22.680  16.591  13.841
HETATM    7  H1  104     1     -21.623  17.681  16.014
HETATM    8  H2  104     1     -22.451  19.218  15.823
HETATM    9  HO  102     1     -21.040  18.108  13.673
HETATM   10  C1  100     2      -4.340 -29.478  45.144
HETATM   11  C2   99     2      -3.051 -29.846  44.395
HETATM   12  OH  101     2      -1.968 -29.072  44.880
HETATM   13  F1  103     2      -4.217 -29.778  46.464
HETATM   14  F2  103     2      -5.396 -30.156  44.621
HETATM   15  F3  103     2      -4.551 -28.140  45.015
HETATM   16  H1  104     2      -3.178 -29.656  43.329
HETATM   17  H2  104     2      -2.829 -30.908  44.511
HETATM   18  HO  102     2      -2.315 -28.222  45.119
HETATM   19  C1  100     3     -49.455 -17.542 -31.718
HETATM   20  C2   99     3     -49.981 -18.984 -31.736
HETATM   21  OH  101     3     -48.905 -19.897 -31.607
HETATM   22  F1  103     3     -48.867 -17.273 -30.521
HETATM   23  F2  103     3     -50.474 -16.668 -31.929
HETATM   24  F3  103     3     -48.527 -17.405 -32.704
...

谢谢

这是解决此问题的一种方法 while read 循环,grepsed

counter=0
while read line; do 
  # if a line has CT, CF, F in it... 
  if echo $line | grep -Pq '(CT|HC|F) '; then 
    # increment the counter and...
    counter=$((counter+1))

    # replace the 15th character with the counter!
    echo $line | sed "s/./$counter/15"
  else 

    # otherwise, reset the counter, and echo the line
    counter=0
    echo $line
  fi
done < molecule.txt

然后您可以将其通过管道传输到另一个文件或标准输出!

你可以使用 awksed 更容易,但我毫不怀疑如果你真的想的话,它也可以在 sed 中完成。

您需要:

  • 打印字段数为 1(或 2 — 小于 3)的行。
  • 当至少有 3 列时,跟踪第 3 列中的最后一个值。
  • 如果当前列是 CT、F 或 HC 之一:
    • 如果第3列的最后一个值不同,将输入的第3列替换为首字母加1;记录下你输出的是1
    • 否则,增加计数并输出第一个字母加上计数器。
  • 否则输出行不变。

在文件 awk.script 中翻译成 awk 脚本可能是:

NF < 3 { print; next }
 != "CT" &&  != "F" &&  != "HC" { print; next }
{ if (old_col3 != ) { counter = 0 }
  old_col3 = 
   = substr(, 1, 1) ++counter
  print
}

并且,当您的数据文件中的 运行(非原创地命名为 data)时,我得到:

$ awk -f awk.script data
COMPND
SOURCE    
HETATM 1 C1 100 1 -23.207 17.632 14.543
HETATM 2 C2 99 1 -22.069 18.353 15.280
HETATM    3  OH  101     1     -21.074  18.762  14.358
HETATM 4 F1 103 1 -23.816 18.483 13.675
HETATM 5 F2 103 1 -24.119 17.162 15.433
HETATM 6 F3 103 1 -22.680 16.591 13.841
HETATM 7 H1 104 1 -21.623 17.681 16.014
HETATM 8 H2 104 1 -22.451 19.218 15.823
HETATM    9  HO  102     1     -21.040  18.108  13.673
HETATM 10 C1 100 2 -4.340 -29.478 45.144
HETATM 11 C2 99 2 -3.051 -29.846 44.395
HETATM   12  OH  101     2      -1.968 -29.072  44.880
HETATM 13 F1 103 2 -4.217 -29.778 46.464
HETATM 14 F2 103 2 -5.396 -30.156 44.621
HETATM 15 F3 103 2 -4.551 -28.140 45.015
HETATM 16 H1 104 2 -3.178 -29.656 43.329
HETATM 17 H2 104 2 -2.829 -30.908 44.511
HETATM   18  HO  102     2      -2.315 -28.222  45.119
HETATM 19 C1 100 3 -49.455 -17.542 -31.718
HETATM 20 C2 99 3 -49.981 -18.984 -31.736
HETATM   21  OH  101     3     -48.905 -19.897 -31.607
HETATM 22 F1 103 3 -48.867 -17.273 -30.521
HETATM 23 F2 103 3 -50.474 -16.668 -31.929
HETATM 24 F3 103 3 -48.527 -17.405 -32.704
$

这不会保留修改行中的所有间距,但在其他方面是您需要的。如果确实需要保留间距,则必须编写 printf() 语句来正确格式化字段(代替最后一段代码中的 print

printf("%s %4s %3s %4s %5s %11s %7s %7s\n", , , , , , , , );

这确实保留了间距,但总体上降低了代码的健壮性。它利用 属性 比 %ns 中的 n 短的字符串是右对齐的。结果:

COMPND
SOURCE    
HETATM    1  C1  100     1     -23.207  17.632  14.543
HETATM    2  C2   99     1     -22.069  18.353  15.280
HETATM    3  OH  101     1     -21.074  18.762  14.358
HETATM    4  F1  103     1     -23.816  18.483  13.675
HETATM    5  F2  103     1     -24.119  17.162  15.433
HETATM    6  F3  103     1     -22.680  16.591  13.841
HETATM    7  H1  104     1     -21.623  17.681  16.014
HETATM    8  H2  104     1     -22.451  19.218  15.823
HETATM    9  HO  102     1     -21.040  18.108  13.673
HETATM   10  C1  100     2      -4.340 -29.478  45.144
HETATM   11  C2   99     2      -3.051 -29.846  44.395
HETATM   12  OH  101     2      -1.968 -29.072  44.880
HETATM   13  F1  103     2      -4.217 -29.778  46.464
HETATM   14  F2  103     2      -5.396 -30.156  44.621
HETATM   15  F3  103     2      -4.551 -28.140  45.015
HETATM   16  H1  104     2      -3.178 -29.656  43.329
HETATM   17  H2  104     2      -2.829 -30.908  44.511
HETATM   18  HO  102     2      -2.315 -28.222  45.119
HETATM   19  C1  100     3     -49.455 -17.542 -31.718
HETATM   20  C2   99     3     -49.981 -18.984 -31.736
HETATM   21  OH  101     3     -48.905 -19.897 -31.607
HETATM   22  F1  103     3     -48.867 -17.273 -30.521
HETATM   23  F2  103     3     -50.474 -16.668 -31.929
HETATM   24  F3  103     3     -48.527 -17.405 -32.704

因为当您达到 10,000 条记录时,HETATM 列和后面的数字列合并为一个列:

HETATM   21  OH  101     3     -48.905 -19.897 -31.607
…
HETATM 9999  HO  102  1111     -24.504 -16.257 -35.613
HETATM10000  CT  100  1112       9.045  23.978  29.038
HETATM10001  CT   99  1112      10.488  24.501  29.083
HETATM10002  OH  101  1112      11.370  23.545  28.522
HETATM10003  F   103  1112       8.650  23.804  27.749
HETATM10004  F   103  1112       8.209  24.855  29.654
HETATM10005  F   103  1112       8.996  22.779  29.679

不清楚当数字达到 100,000 及以上时会发生什么。但是,可以(在大多数情况下)通过计算列数并适当地工作来处理它。

NF < 7 { print; next }
NF == 8 &&  != "CT" &&  != "F" &&  != "HC" { print; next }
NF == 7 &&  != "CT" &&  != "F" &&  != "HC" { print; next }
NF == 8 {
          if (old_mark != ) { counter = 0 }
          old_mark = 
           = substr(, 1, 1) ++counter
          printf("%s %4s %3s %4s %5s %11s %7s %7s\n", , , , , , , , );
        }
NF == 7 {
          if (old_mark != ) { counter = 0 }
          old_mark = 
           = substr(, 1, 1) ++counter
          printf("%s %3s %4s %5s %11s %7s %7s\n", , , , , , , );
        }

注意 'column number neutral' 名称 old_mark 的使用。如果第 9,999 行包含 CT 并且第 10,000 行也包含 CT,则映射需要是连续的(C1,C2)等。您可以使用:

NF < 7 { print; next }
NF == 8 &&  != "CT" &&  != "F" &&  != "HC" { print; next }
NF == 7 &&  != "CT" &&  != "F" &&  != "HC" { print; next }
{
    colnum = NF - 5
    if (old_mark != $colnum) { counter = 0 }
    old_mark = $colnum
    $colnum = substr($colnum, 1, 1) ++counter
    if (NF == 7)
        printf("%s %3s %4s %5s %11s %7s %7s\n", , , , , , , );
    else
        printf("%s %4s %3s %4s %5s %11s %7s %7s\n", , , , , , , , );
}

可能有一种方法可以使用一个 printf() 调用,但我怀疑是否值得这样做。