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

标签 linux awk sed protein-database

我有一个包含大约 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
...

谢谢

最佳答案

使用 awk 比使用 sed 更容易,但我毫不怀疑如果您真的需要,也可以在 sed 中完成到。

你需要:

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

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

NF < 3 { print; next }
$3 != "CT" && $3 != "F" && $3 != "HC" { print; next }
{ if (old_col3 != $3) { counter = 0 }
  old_col3 = $3
  $3 = substr($3, 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", $1, $2, $3, $4, $5, $6, $7, $8);

这确实保留了间距,但总体上降低了代码的健壮性。它利用了 %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 && $3 != "CT" && $3 != "F" && $3 != "HC" { print; next }
NF == 7 && $2 != "CT" && $2 != "F" && $2 != "HC" { print; next }
NF == 8 {
          if (old_mark != $3) { counter = 0 }
          old_mark = $3
          $3 = substr($3, 1, 1) ++counter
          printf("%s %4s %3s %4s %5s %11s %7s %7s\n", $1, $2, $3, $4, $5, $6, $7, $8);
        }
NF == 7 {
          if (old_mark != $2) { counter = 0 }
          old_mark = $2
          $2 = substr($2, 1, 1) ++counter
          printf("%s %3s %4s %5s %11s %7s %7s\n", $1, $2, $3, $4, $5, $6, $7);
        }

请注意“列号中性”名称 old_mark 的使用。如果第 9,999 行包含 CT 并且第 10,000 行也包含 CT,则映射需要是连续的(C1,C2)等。您可以使用:

NF < 7 { print; next }
NF == 8 && $3 != "CT" && $3 != "F" && $3 != "HC" { print; next }
NF == 7 && $2 != "CT" && $2 != "F" && $2 != "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", $1, $2, $3, $4, $5, $6, $7);
    else
        printf("%s %4s %3s %4s %5s %11s %7s %7s\n", $1, $2, $3, $4, $5, $6, $7, $8);
}

可能有一种方法可以使用一个 printf() 调用,但我怀疑它是否值得付出努力。

关于linux - 在 pdb 文件的每 2-3-4 行使用 awk 和 sed 替换文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30247105/

相关文章:

Linux 内核 regmap irq 处理程序有问题

php - MySQL 远程访问、telnet 和 ping 无法跨具有相同顶级域的服务器工作

linux - 如何在 RHEL 5 (Linux) 中卸载然后安装 Java 我遇到错误?

linux - 我如何从 Linux 中的命令行替换文件中的此文本?

bash - 在第二个模式 sed 之后删除行

linux - 是否可以让vim显示行号?

regex - 用awk更改字符串的大小写

linux - 如何grep一个文件,它是一个从awk重定向的字符串

shell - shell中多个文件的平均值

regex - 使用 sed 将单词 "ora.(anything)"后的空格替换为新行和单词 "ora.(anything)"