在Shell for循环中使用awk指定列打印时出现语法错误求助
这个问题我之前也碰到过,核心问题在于bash变量在单引号里不会被解析,而且你尝试的变量嵌套写法在bash里也不生效,咱们一步步拆解解决:
首先说你原来写法的问题:
- 当你用
awk '{ print $${i} }'时,单引号包裹的内容bash会原封不动传给awk,所以awk看到的是print $${i},这显然是语法错误,因为awk不认识${i}这种写法。 - 而
${${i}}是bash里不支持的变量嵌套展开方式,bash没法直接解析这种嵌套的变量名。
接下来给你几种可行的解决办法,按推荐程度排序:
方法1:用awk的-v选项传递变量(最推荐)
这种方式把bash的循环变量i传递给awk内部的变量,完全避免引号转义的麻烦,可读性和安全性都最高:
for i in {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,21}; do awk -v col="$i" '{ print $col }' COLVAR > tmp_${i} done
这里-v col="$i"把bash的i值赋值给awk的col变量,awk里用$col就能直接引用第col列,非常直观。
方法2:用双引号包裹awk脚本(需要转义$)
如果坚持要在bash里直接展开列号,就把awk脚本放在双引号里,同时转义$让bash先解析变量:
for i in {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,21}; do awk "{ print \$$i }" COLVAR > tmp_${i} done
这里\$$i会被bash先解析成$2(当i=2时),然后awk拿到的就是print $2,就能正常打印第2列了。不过这种写法如果变量里有特殊字符容易出问题,不如方法1稳妥。
方法3:一次awk处理所有列(最高效)
其实你完全不需要循环调用awk,一次awk就能生成所有需要的文件,效率更高(尤其是处理大文件的时候):
awk -v cols="2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 21" ' BEGIN { split(cols, col_arr) } { for (idx in col_arr) { col = col_arr[idx] print $col > "tmp_" col } }' COLVAR
这个脚本在BEGIN块里把列号字符串拆成数组,然后每一行都遍历所有列号,把对应列的内容写入对应的tmp文件,只启动一次awk,比循环多次调用快很多。
内容的提问来源于stack exchange,提问作者Soumendranath Bhakat
相关产品推荐
相关产品推荐

