SED 特定色谱柱更换

SED 特定色谱柱更换

如何使用 Sed 命令将第 4 列中的 A 替换为 RA? (这些列之间包含多个空格)

ATOM     32  P     A     2       6.882  -5.338   6.560  1.00  0.00           P  
ATOM     33  OP1   A     2       7.505  -5.970   7.750  1.00  0.00           O  
ATOM     34  OP2   A     2       5.404  -5.201   6.610  1.00  0.00           O  
TER

可以通过awk '{gsub("A","RA",$4)}1' a.txt > b.txt

答案1

由于间距必须保持固定,也许这意味着整个布局是固定的,然后这可能就是您正在寻找的使用支持 -E 的 sed 来启用 ERE,例如 GNU sed 或 OSX/BSD sed:

$ sed -E 's/(.{17})A /\1RA/' file
ATOM     32  P     RA    2       6.882  -5.338   6.560  1.00  0.00           P
ATOM     33  OP1   RA    2       7.505  -5.970   7.750  1.00  0.00           O
ATOM     34  OP2   RA    2       5.404  -5.201   6.610  1.00  0.00           O
TER

或使用任何 POSIX sed:

$ sed 's/\(.\{17\}\)A /\1RA/' file
ATOM     32  P     RA    2       6.882  -5.338   6.560  1.00  0.00           P
ATOM     33  OP1   RA    2       7.505  -5.970   7.750  1.00  0.00           O
ATOM     34  OP2   RA    2       5.404  -5.201   6.610  1.00  0.00           O
TER

编辑:您的输入似乎不符合PDB标准@bushman 之前发布过,但如果确实如此,您可以使用以下方法使用标准中的信息f[]按标签/名称创建字段数组,按标签/名称修改它们(而不是它们在输入中的相对位置) ),并以相同的固定宽度格式打印它们:

$ cat tst.awk
BEGIN {
    # Record Format (copied from http://www.wwpdb.org/documentation/file-format-content/format33/sect9.html#ATOM)
    #
    #                 COLUMNS        DATA  TYPE    FIELD        DEFINITION
    #                 -------------------------------------------------------------------------------------
    flds[++numFlds]="  1 -  6        Record name   ATOM  "
    flds[++numFlds]="  7 - 11        Integer       serial       Atom  serial number."
    flds[++numFlds]=" 13 - 16        Atom          name         Atom name."
    flds[++numFlds]=" 17             Character     altLoc       Alternate location indicator."
    flds[++numFlds]=" 18 - 20        Residue name  resName      Residue name."
    flds[++numFlds]=" 22             Character     chainID      Chain identifier."
    flds[++numFlds]=" 23 - 26        Integer       resSeq       Residue sequence number."
    flds[++numFlds]=" 27             AChar         iCode        Code for insertion of residues."
    flds[++numFlds]=" 31 - 38        Real(8.3)     x            Orthogonal coordinates for X in Angstroms."
    flds[++numFlds]=" 39 - 46        Real(8.3)     y            Orthogonal coordinates for Y in Angstroms."
    flds[++numFlds]=" 47 - 54        Real(8.3)     z            Orthogonal coordinates for Z in Angstroms."
    flds[++numFlds]=" 55 - 60        Real(6.2)     occupancy    Occupancy."
    flds[++numFlds]=" 61 - 66        Real(6.2)     tempFactor   Temperature  factor."
    flds[++numFlds]=" 77 - 78        LString(2)    element      Element symbol, right-justified."
    flds[++numFlds]=" 79 - 80        LString(2)    charge       Charge  on the atom."

    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        fld = flds[fldNr]

        cols = substr(fld,1,16)
        gsub(/ /,"",cols)
        n = split(cols,begEnd,/-/)

        tag  = substr(fld,31,13)
        gsub(/ /,"",tag)

        tags[fldNr] = tag
        begs[tag] = begEnd[1]
        wids[tag] = begEnd[n] - begEnd[1] + 1

        # Uncomment this if interested in the values the arrays contain:
        # print "<" fldNr "><" tags[fldNr] "><" begs[tag] "><" wids[tag] ">" | "cat>&2"
    }
}

{
    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        tag = tags[fldNr]
        f[tag] = substr($0,begs[tag],wids[tag])
        gsub(/^ +| +$/,"",f[tag])
    }
}

f["resName"] == "A" { f["resName"] = "RA" }     # this is where you can change a field by its tag/name

{
    for (fldNr=1; fldNr<=numFlds; fldNr++) {
        tag = tags[fldNr]
        printf "%-*s", wids[tag], f[tag]
    }
    print ""
}
$
$ awk -f tst.awk file
ATOM  32   P    RA  2    6.882   -5.338  6.560   1.00  0.00  P
ATOM  33   OP1  RA  2    7.505   -5.970  7.750   1.00  0.00  O
ATOM  34   OP2  RA  2    5.404   -5.201  6.610   1.00  0.00  O
TER

显然,这对于您现在正在尝试做的事情来说有点矫枉过正,但这是一个需要牢记的好通用方法,它解决了您在您感兴趣的字段之前讨论的任何其他方法可能遇到的问题。

答案2

使用sed

sed -E "s/^(([^ ]+ +){3})A  /\1RA /" file1

演练

从输入行开始^

([^ ] +)表示将一组捕获( )为连续的非空格字符序列,[^ ]+后跟另一个连续的空格序列+

抓住这个组的重复{3},所以下一个字符将出现在你的第四个字段中

通过包裹将所有这三个重复组合在一起( )

如果现在存储的“超级组”\1后面跟着A(即带有 2 个空格的 A),则将其替换为\1RA(即只有 1 个尾随空格,因此您保持字符数相同)

答案3

如果您只是希望通过列替换生成漂亮的打印表格输出,则在命令中添加以下内容awk即可实现此目的:

awk ' gsub("A","RA",$4){for (i=1;i<=NF;i++){printf("%-9s",$i)}print "" }' a.txt > b.txt

输出cat b.txt如下:

ATOM     32       P        RA       2        6.882    -5.338   6.560    1.00     0.00     P
ATOM     33       OP1      RA       2        7.505    -5.970   7.750    1.00     0.00     O
ATOM     34       OP2      RA       2        5.404    -5.201   6.610    1.00     0.00     O

如果您正在寻找每列之间更具体的间距,则必须放弃循环printf并为每个字段分配单独的间距。


如果您正在寻找一种非常快速的sed替代方案,您始终可以这样做:

sed 's/ A /RA/' a.txt > b.txt

但这将用“RA”替换文件中的每个“A”,不限于第四列,因为sed使用与 不同的字段分割方法awk。尽管如此,使用上面的sed命令将会得到cat b.txt如下所示的结果:

ATOM     32  P    RA    2       6.882  -5.338   6.560  1.00  0.00           P
ATOM     33  OP1  RA    2       7.505  -5.970   7.750  1.00  0.00           O
ATOM     34  OP2  RA    2       5.404  -5.201   6.610  1.00  0.00           O

答案4

方法 1:不带反向引用的 GNU sed

$ sed -re '
     h
     s/\S+/\n&\n/4
     s/\nA\n/RA/;t
     g
 ' file

方法 2:带有反向引用的 GNU sed。

 $ sed -re '
      s/^(\s*(\S+\s+){3})(A(\s|$))/\1R\3/
 ' file

请注意,这两种 sed 解决方案都可以兼容 Posix,但它们会以代码清晰度为代价,并可能导致反斜杠炎。

方法 3:Perl 使用类似 awk 的字段,但有一点不同,这个字段既存储字段值又存储分隔符。因此,第 4 个字段变为第 8 个字段,如果使用零索引数组,则为 7。

$ perl -F'/(\S+)/' -lane '
     $F[7] =~ s/^A$/RA/;
     print @F;\
 ' file 

相关内容