【问题标题】:Is it possible to pass a bash array as a variable to awk?是否可以将 bash 数组作为变量传递给 awk?
【发布时间】:2011-11-03 21:05:18
【问题描述】:

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

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

现在我想从包含几行格式如下的文件中提取信息:

原子 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 的文件,但不会生成同时具有链 A 或 B 的文件。此外,它不会生成数组“chain”和“hresname”的所有可能组合,它只是将“hresname”添加到只有一个“chain”可用的文件中。

【问题讨论】:

  • 您的意思是 $5==c 在您的 awk 代码中吗? $5=c 将 var 'c' 中的值分配给当前记录的第 5 列。更一般地说,为什么不在 awk 中构建一些代码来解析您的输入文件,并且如果可能的话,更改输入文件的格式以使其在 awk 中时需要更少的解析。祝你好运!
  • 感谢您发现这个错误,Shawn Chin。改变输入的格式不是一个大问题。至于你的第一个建议,由于两个原因,它有点复杂:1)这个脚本属于一个长的 bash 管道; 2)我根本不知道任何 awk。
  • 鉴于上述输入数据,您能否发布所需的最终输出以及管道的伪代码。您真的是说每条“记录”都在一行(根据您的示例数据)吗?您展示的 awk 是完全惯用的,但您可能会从使用关联数组和 printf 语句中受益。
  • 我也不明白你想要什么。当您说第 5 列应该同时匹配 A 和 B 时,这是什么意思?是值“AB”还是“BA”?您确实需要显示更大的样本输入。
  • 我仍在尝试破译您想要实现的目标,即您的输入、输出是什么样的。但是我注意到 AWK 表达式 '{ if($1 == "ATOM" && $5==c) { print $0 } }' 可以缩短为 '$1 == "ATOM" && $5==c' 这将做同样的事情东西,但更紧凑。

标签: arrays bash loops awk


【解决方案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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多