可以将bash数组作为变量传递给awk吗?

3
我有大量数据要从文本文件中导入。这些文件已经预先格式化,以便我可以将每个列作为bash数组导入:

2GYS chain=(A B) hresname=(BMA FUC NAG NDG) hresnumber=( ) hatom=( )

现在,我想从包含多行此类格式的文件中提取信息:

ATOM 1 N THR A 4 30.127 13.123 1.297 1.00 39.96 N

例如,我想提取所有第一列为ATOM且第五列与链数组匹配(在本例中,它将是A和B)的行。
更新。这是我尝试过的内容:
for c in "${chain[@]}" ; do
  awk -v pdbid="$pdbid" -v c="$c" '{ if($1 == "ATOM" && $5==c) { print $0 } }' ${pdbid}.pdb >> ../../properpdb/${pdbid}_${c}.pdb
done

for c in "${chain[@]}" ; do
 for r in "${hresname[@]}" ; do
   awk -v pdbid="$pdbid" -v c="$c" -v r="$r" '{ if($1 == "HETATM" && $5==c && $4==r) { print $0 } }' ${pdbid}.pdb >> ../../properpdb/${pdbid}_${c}.pdb
 done
done

问题在于,正如预期的那样,这会产生仅包含A链或B链的文件,而不是同时包含两者的文件。此外,它也没有产生“chain”和“hresname”数组的所有可能组合,它只是将“hresname”添加到仅有一个“chain”的文件中。

1
你的awk代码中 $5==c 是什么意思?$5=c 将变量 'c' 的值赋给当前记录的第5列。更一般地说,为什么不在awk中构建一些代码来解析输入文件,并在可能的情况下更改输入文件的格式,以使其在awk中需要更少的解析。祝好运! - shellter
感谢您发现这个错误,Shawn Chin。更改输入格式并不是一个大问题。至于您的第一个建议,由于两个原因,它有点复杂:1)此脚本属于长时间的bash管道;2)我根本不知道awk。 - mirix
根据上述输入数据,你能发布所需的最终输出以及你的管道的伪代码吗?根据你提供的示例数据,你真的希望每个“记录”都在一行上吗?你展示的awk命令非常符合习惯用法,但是你可能会受益于使用关联数组和printf语句。 - shellter
1
我也不明白你想要什么。当你说第五列应该匹配A和B时,这意味着什么?是值“AB”还是“BA”?你需要展示一个更大的输入样本。 - glenn jackman
1
我仍在尝试解密你想要实现什么,即你的输入和输出是什么样子。但是我注意到 AWK 表达式 '{ if($1 == "ATOM" && $5==c) { print $0 } }' 可以缩短为 '$1 == "ATOM" && $5==c',这将完成相同的操作,但更为简洁。 - Hai Vu
1个回答

1
我的解决方案是在bash中构建awk脚本的一部分,具体来说是匹配函数。
您似乎想要与$1 == "ATOM" && ($5==c[0] || $5==c[1]...) {print $0}匹配的字段导出到文件中。
在bash中,将匹配函数构建为:
cmatch="\$5==\"${chain[0]}\""
for element in $(seq 1 $((${#chain[@]} - 1))); do cmatch+=" || \$5==\"${chain[$element]}\""; done
#cmatch should now be of the form "$5==A || $5==B"

#do the same thing for rmatch
rmatch="\$4==\"${hresname[0]}\""
for element in $(seq 1 $((${#hresname[@]} - 1))); do rmatch+=" || \$4==\"${hresname[$element]}\""; done

现在您的 awk 脚本可以进行调整,以包含所需的位:(引号仍然很麻烦,因为您需要确保 $1 不受干扰地传递给 awk,但 $cmatch 是被评估的。)
rmatch='$1=="HETATM" && ('"$cmatch"') && ('"$rmatch"')'  #order is important here :)
cmatch='$1=="ATOM" && ('"$cmatch"')'

现在你的匹配脚本应该已经完成了。

awk "$cmatch" ${pdbid}.pdb >> ../../properpdb/${pdbid}_c.pdb
awk "$rmatch" ${pdbid}.pdb >> ../../properpdb/${pdbid}_c.pdb

我不太理解输出文件名../../properpdb/${pdbid}_${c}.pdb,因为这似乎表明每个c元素都有单独的文件,而这不是你想要的吧?

如果你想按c元素分割它们,那么稍微简单一些,像上面那样构建rmatch数组,然后做类似以下的操作:

for c in "${chain[@]}" ; do
  awk -v c="$c" '$1=="ATOM" && $5==c' ${pdbid}.pdb  >> ../../properpdb/${pdbid}_${c}.pdb
  awk -v c="$c" '$1=="HETATM" && $5==c && ('"$rmatch"')' ${pdbid}.pdb  >> ../../properpdb/${pdbid}_${c}.pdb
done

如果您想首先获取所有的ATOM元素,或者...

for c in "${chain[@]}" ; do
  awk -v c="$c" '$5==c && ($1=="ATOM" || ($1=="HETATM" && ('"$rmatch"')))' ${pdbid}.pdb  >> ../../properpdb/${pdbid}_${c}.pdb
done

如果你想要它们混合在一起


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接