Bash 如果字符串在同一行中匹配,则基于位置替换文本块
我有两套PDB文件(这是一种标准格式,不能修改)。第一组是这样的: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
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"