这是文件
a.pdb
:
ATOM 1 N ARG 1 0.000 0.000 0.000 1.00 0.00 N
ATOM 2 H1 ARG 1 0.000 0.000 0.000 1.00 0.00 H
ATOM 3 H2 ARG 1 0.000 0.000 0.000 1.00 0.00 H
ATOM 4 H3 ARG 1 0.000 0.000 0.000 1.00 0.00 H
这是文件
a.xyz
:
16.388 -5.760 -23.332
17.226 -5.608 -23.768
15.760 -5.238 -23.831
17.921 -5.926 -26.697
我想用
a.pdb
列替换 a.xyz
的第 6,7 和第 8 列。 更换后,我需要维护 a.pdb
的制表符/空格/列。
我已经尝试过:
awk 'NR==FNR {fld1[NR]=$1; fld2[NR]=$2; fld3[NR]=$3; next} {$6=fld1[FNR]; $7=fld2[FNR]; $8=fld3[FNR]}1' a.xyz a.pdb
但它不保留格式。
这正是 GNU awk 中 split() 的第四个参数的发明目的:
gawk '
NR==FNR { pdb[NR]=$0; next }
{
split(pdb[FNR],flds,FS,seps)
flds[6]=$1
flds[7]=$2
flds[8]=$3
for (i=1;i in flds;i++)
printf "%s%s", flds[i], seps[i]
print ""
}
' a.pdb a.xyz
ATOM 1 N ARG 1 16.388 -5.760 -23.332 1.00 0.00 N
ATOM 2 H1 ARG 1 17.226 -5.608 -23.768 1.00 0.00 H
ATOM 3 H2 ARG 1 15.760 -5.238 -23.831 1.00 0.00 H
ATOM 4 H3 ARG 1 17.921 -5.926 -26.697 1.00 0.00 H
不是通用解决方案,但这可能适用于这种特殊情况:
awk 'NR==FNR{for(i=6; i<=8; i++) A[FNR,i]=$(i-5); next} {for(i=6; i<=8; i++) sub($i,A[FNR,i])}1' file2 file1
或
awk '{for(i=6; i<=8; i++) if(NR==FNR) A[FNR,i]=$(i-5); else sub($i,A[FNR,i])} NR>FNR' file2 file1
不过,还是有一点转变。我们需要知道字段宽度以防止这种情况发生。
-- 或者也许带有子字符串:
awk 'NR==FNR{A[FNR]=$0; next} {print substr($0,1,p) FS A[FNR] substr($0,p+length(A[FNR]))}' p=33 file2 file1
-- 在OP的原始解决方案中更改它:
awk 'NR==FNR {fld1[NR]=$1; fld2[NR]=$2; fld3[NR]=$3; next} {sub($6,fld1[FNR]); sub($7,fld2[FNR]); sub($8,fld3[FNR])}1' file file1
与前 2 个建议具有相同的限制。
因此 1、2 和 4 使用
sub
来替换,这不是防水解决方案,因为早期的字段可能会干扰并且它使用正则表达式而不是字符串(因此正则表达式点恰好与实际点匹配),但是有了这个特定的输入,它可能会成功..
可能是nr。 3将是一个更万无一失的方法..
--编辑-- 我认为这适用于给定的输入:
awk 'NR==FNR{A[FNR]=$1 " " $2 " " $3; next} {print substr($0,1,p) A[FNR] substr($0,p+length(A[FNR]))}' p=32 file2 file1
但我认为需要类似
printf
或 sprint
的格式才能使其万无一失。
所以,也许是这样的:
awk 'NR==FNR{A[FNR]=sprintf("%7.3f %7.3f %8.4f", $1, $2, $3); next} {print substr($0,1,p) A[FNR] substr($0,p+length(A[FNR]))}' p=31 file2 file1
或者不在一行:
awk '
NR==FNR {
A[FNR]=sprintf("%7.3f %7.3f %8.4f", $1, $2, $3)
next
}
{
print substr($0,1,p) A[FNR] substr($0,p+length(A[FNR]))
}
' p=31 file2 file1
你可以试试这个
paste -d' ' test4 test5 |awk '{print $1,$2,$3,$4,$5,$12,$13,$14,$9,$10,$11}'
这是一个 POSIX awk 解决方案。没有 gawk 的扩展
split()
,我们必须自己跟踪间距:
awk '
NR==FNR {fld1[NR]=$1; fld2[NR]=$2; fld3[NR]=$3; next}
{
tmp = $0
n = 0
out = ""
for (i = 1; i < NF; i++) {
tmp = substr(tmp, n)
spacing = substr(tmp, 1, index(tmp, $i) - 1)
n = length(spacing $i) + 1
out = out spacing
if (i == 6) out = out fld1[FNR]
else if (i == 7) out = out fld2[FNR]
else if (i == 8) out = out fld3[FNR]
else out = out $i
}
$0 = out substr(tmp, n)
}
1
' a.xyz a.pdb
ATOM 1 N ARG 1 16.388 -5.760 -23.332 1.00 0.00 N
ATOM 2 H1 ARG 1 17.226 -5.608 -23.768 1.00 0.00 H
ATOM 3 H2 ARG 1 15.760 -5.238 -23.831 1.00 0.00 H
ATOM 4 H3 ARG 1 17.921 -5.926 -26.697 1.00 0.00 H
它会跟踪每个字段之间的间距,以便在替换后可以重新组装。
首先,我们将临时变量
tmp
设置为整条线$0
,并初始化位置跟踪器n
和最终组装out
。然后我们循环遍历 awk 已经解析的每个字段。循环第一次运行中的第一个 tmp
赋值不会执行任何操作,因为 n
为零。然后我们将 spacing
设置为 tmp
位于我们所在字段的第一个实例之前的部分(其值为 $i
)。接下来,n
更新到新位置,以便我们可以在下一次迭代中缩小 tmp
。 out
获得我们刚刚保存的间距。
现在,我们终于可以执行您想要执行的操作了。我们检查
$6 = fld1[FNR]
并将值附加到 i
,而不是 out
。如果它不是我们想要的替换之一,我们只需附加原始值。
循环之后,我们将
$0
分配给准备好的 out
,并且任何尾随字段分隔符可能会持续存在(可能什么也没有)。独立的 1
子句会导致其打印。
您很可能可以在运行更简单的 awk 代码后重新格式化输出。只需通过管道即可
column -t
:
awk 'NR==FNR {fld1[NR]=$1; fld2[NR]=$2; fld3[NR]=$3; next} {$6=fld1[FNR]; $7=fld2[FNR]; $8=fld3[FNR]}1' a.xyz a.pdb |column -t
ATOM 1 N ARG 1 16.388 -5.760 -23.332 1.00 0.00 N
ATOM 2 H1 ARG 1 17.226 -5.608 -23.768 1.00 0.00 H
ATOM 3 H2 ARG 1 15.760 -5.238 -23.831 1.00 0.00 H
ATOM 4 H3 ARG 1 17.921 -5.926 -26.697 1.00 0.00 H
另请参阅我自己的 columns 脚本,我更喜欢它而不是
column -t
,因为它更强大并且可以处理颜色和选项卡。