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

Bash 如果字符串在同一行中匹配,则基于位置替换文本块,bash,awk,position,replace,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

我有两套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列也可能发生这种情况):

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

读行时
;做
原子=$(回声“$线”|切割-c13-16)
coord=$(grep-i“$atom”${ligand}{u${chain}}}uDock.tmp|cut-c32-54)
echo“$line”| sed-r”s/^(.{31})(.{23})/\1${coord}/“>>${ligand}{chain}}u dock.pdb
完成<${ligand}{chain}\u ref.pdb

我可能会选择一种愚蠢的方法来解决它:使用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 

这也行。我们没有打印每一列,而是将OFS变量设置为“\t”

更新:在OFS变量的tab旁边添加了几个空格。这使输出之间有足够的间距

[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

我猜测了正确的字段宽度,但如果调整正确,这应该可以工作

#!/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"
#/usr/bin/env bash
file1=“$1”
file2=“$2”
fw=(764469795164)
而IFS=读取-r-a f2_线;做
设pos=0
f2_字段=()
宽度单位为“${fw[@]}”;做
f2_字段=(“${f2_字段[@]}”“${f2_行:${pos}:${width}”)
设pos+=宽度
完成
printf'%s'${f2_字段[@]:0:6}
orig=1
而IFS=read-r-a f1_行;做
设pos=0
f1_字段=()
宽度单位为“${fw[@]}”;做
f1_字段=(“${f1_字段[@]}”“${f1_行:${pos}:${width}”)
设pos+=宽度
完成
如果[“${f1_字段[2]}”=“${f2_字段[2]}”];然后
原=
printf'%s'${f1_字段[@]:6:3}
打破
fi
完成<“$file1”
如果[!-z“$orig”];然后
printf'%s'${f2_字段[@]:6:3}
fi
printf'%s'${f2_字段[@]:9}
printf'\n'
完成<“$file2”
当然,这不是很有效


编辑:Oops,必须在第14行的s/5/6/。现在可以工作。

当字段3不匹配时,您想做什么?无。保持原始坐标。然而,总有一场比赛。请记住,在某些文件中,第1列可以合并到第2列,第4列可以合并到第5列。每个字段的确切宽度都会很有帮助。如果可以教授关于固定宽度字段的
join(1)
,这将非常简单。您的解决方案,仅供参考,将无法在每行末尾保留尾随空格。我想这对格式很重要。您不需要行尾反斜杠,也不需要显式地
print$0
——只需
print
。默认的字段分隔符是“空白”,因此您不需要使用
-F
。此解决方案有一个问题,这就是我通常在处理awk和PDB文件时遇到问题的原因。有时会合并列。当第2列的值大于99999或第4列的值大于999或9999时,通常会出现这种情况,例如:HETATM 2804 PG ANP A1001 23.808 17.953 28.350 1.00 52.23 p这就是为什么我需要一个严格依赖于位置而不依赖于分隔符的方法。
[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
#!/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"