如果字符串在同一行中匹配,则基于位置的文本块替换

时间:2011-11-28 11:35:58

标签: bash awk position replace

我有两套PDB文件(这是一种无法修改的标准格式)。第一组就像:

ATOM     18  C33 Q58 d  91     -25.677   3.886 -30.044  1.00  0.00           C  
ATOM     19  C34 Q58 d  91     -24.704   4.881 -29.447  1.00  0.00           C  
ATOM     20  C35 Q58 d  91     -23.382   4.873 -30.182  1.00  0.00           C  
ATOM     21  C8  Q58 d  91     -20.295  11.484 -33.616  1.00  0.00           C  
ATOM     22  C7  Q58 d  91     -19.198  12.305 -33.381  1.00  0.00           C  
ATOM     23  C3  Q58 d  91     -18.213  12.498 -34.383  1.00  0.00           C   

第二个问题是:

HETATM 2686  C7  589 A   1     -19.344  12.177 -33.319  1.00 25.88           C  
HETATM 2687  C8  589 A   1     -20.388  11.319 -33.511  1.00 26.31           C  
HETATM 2688  C9  589 A   1     -20.364  10.691 -34.747  1.00 26.14           C  
HETATM 2689  C10 589 A   1     -19.402  10.845 -35.729  1.00 26.34           C  
HETATM 2690  N11 589 A   1     -21.334  11.123 -32.604  1.00 26.22           N  
HETATM 2691  C12 589 A   1     -21.713   9.967 -32.081  1.00 25.65           C  

每列由可变数量的空格分隔,以使其内容占据特定的位置范围。

第7-9列表示笛卡尔空间中的x,y,z坐标。对于所有第3列(原子类型)匹配,我想用文件1中的坐标替换文件2的坐标。

例如,在示例中,输出文件2将是:

HETATM 2686  C7  589 A   1     -19.198  12.305 -33.381  1.00 25.88           C  
HETATM 2687  C8  589 A   1     -20.295  11.484 -33.616  1.00 26.31           C  
HETATM 2688  C9  589 A   1     -20.364  10.691 -34.747  1.00 26.14           C  
HETATM 2689  C10 589 A   1     -19.402  10.845 -35.729  1.00 26.34           C  
HETATM 2690  N11 589 A   1     -21.334  11.123 -32.604  1.00 26.22           N  
HETATM 2691  C12 589 A   1     -21.713   9.967 -32.081  1.00 25.65           C  

请注意前两行(原子C7和C8)的坐标是如何变化的。

我尝试过awk,但它似乎过于依赖于分隔符,这在这个例子中并不好。第3列(原子类型)始终位于14-16位置,而3个坐标列位于32至54位。

注意:在某些情况下,某些列可能会合并。例如,在此示例中,第5列和第6列已合并(这也可能发生在第1列和第2列):

HETATM 2804  PG  ANP A1001      23.808  17.953  28.350  1.00 52.23           P  

我的解决方案到目前为止(缓慢但有效):

while read line ; do
atom=$(echo "$line" | cut -c13-16)
coord=$(grep -i "$atom" ${ligand}_${chain}_dock.tmp | cut -c32-54)
echo "$line" | sed -r "s/^(.{31})(.{23})/\1${coord}/" >> ${ligand}_${chain}_dock.pdb
done < ${ligand}_${chain}_ref.pdb

3 个答案:

答案 0 :(得分:3)

我可能会选择一种愚蠢的方法来解决它:使用printf语句。但它适用于你的例子。

命令:

 awk -F' *' 'NR==FNR{a[$3]=$7;b[$3]=$8;c[$3]=$9;next;}\
{if($3 in a)printf "%s %s  %-3s %s %s %3s %11s %7s %7s %5s %s %11s\n",\
        $1,$2,$3,$4,$5,$6,a[$3],b[$3],c[$3],$10,$11,$12; else print $0}' file1 file2

用你的例子测试:

kent$  awk -F' *' 'NR==FNR{a[$3]=$7;b[$3]=$8;c[$3]=$9;next;}
{if($3 in a)printf "%s %s  %-3s %s %s %3s %11s %7s %7s %5s %s %11s\n",
        $1,$2,$3,$4,$5,$6,a[$3],b[$3],c[$3],$10,$11,$12; else print $0}' file1 file2
HETATM 2686  C7  589 A   1     -19.198  12.305 -33.381  1.00 25.88           C
HETATM 2687  C8  589 A   1     -20.295  11.484 -33.616  1.00 26.31           C
HETATM 2688  C9  589 A   1     -20.364  10.691 -34.747  1.00 26.14           C  
HETATM 2689  C10 589 A   1     -19.402  10.845 -35.729  1.00 26.34           C  
HETATM 2690  N11 589 A   1     -21.334  11.123 -32.604  1.00 26.22           N  
HETATM 2691  C12 589 A   1     -21.713   9.967 -32.081  1.00 25.65           C 

答案 1 :(得分:1)

我猜测了正确的字段宽度,但是如果它们被正确调整就应该有效。

#!/usr/bin/env bash

file1="$1"
file2="$2"

fw=(7 6 4 4 4 6 9 7 9 5 16 4)

while IFS= read -r -a f2_line ; do
    let pos=0
    f2_fields=()
    for width in "${fw[@]}" ; do
            f2_fields=("${f2_fields[@]}" "${f2_line:${pos}:${width}}")
            let pos+=width
    done

    printf '%s' "${f2_fields[@]:0:6}"
    orig=1
    while IFS= read -r -a f1_line ; do
            let pos=0
            f1_fields=()
            for width in "${fw[@]}" ; do
                    f1_fields=("${f1_fields[@]}" "${f1_line:${pos}:${width}}")
                    let pos+=width
            done
            if [ "${f1_fields[2]}" = "${f2_fields[2]}" ] ; then
                    orig=
                    printf '%s' "${f1_fields[@]:6:3}"
                    break
            fi
    done < "$file1"
    if [ ! -z "$orig" ] ; then
            printf '%s' "${f2_fields[@]:6:3}"
    fi
    printf '%s' "${f2_fields[@]:9}"
    printf '\n'
done < "$file2"

当然,效率不高。

编辑:糟糕,不得不在第14行上获得s / 5/6 /。立即行动。

答案 2 :(得分:0)

这也会奏效。我们不是打印每一列,而是将OFS变量设置为“\ t”。

UPDATE:在OFS变量的标签旁边添加了更多空格。这为您的输出提供了足够的间距。

[jaypal:~/Temp]  awk -v OFS="\t    " 'NR==FNR{a[$3]=$7;b[$3]=$8;c[$3]=$9;next} ($3 in a) {$7=a[$3];$8=b[$3];$9=c[$3];print $0;next} {$1=$1}1' file1 file2
HETATM      2686        C7      589     A       1       -19.198     12.305      -33.381     1.00        25.88       C
HETATM      2687        C8      589     A       1       -20.295     11.484      -33.616     1.00        26.31       C
HETATM      2688        C9      589     A       1       -20.364     10.691      -34.747     1.00        26.14       C
HETATM      2689        C10     589     A       1       -19.402     10.845      -35.729     1.00        26.34       C
HETATM      2690        N11     589     A       1       -21.334     11.123      -32.604     1.00        26.22       N
HETATM      2691        C12     589     A       1       -21.713     9.967       -32.081     1.00        25.65       C