在while循环中使用Awk匹配多列文件仅输出表头的问题求助
问题
编写了一个脚本,从var_table.txt的列中读取变量,与data.txt的某一列进行模式匹配,将匹配到的整行写入对应名称的输出文件。目前脚本仅能输出表头(NR==1的内容),无法输出匹配的数据行。
原脚本
while read -r value1 value2 do echo "$value2" awk -F "\t" 'NR==1; $2 == "$value1" { print $0 }' data.txt > "${value2}".out done < "var_table.txt"
示例数据
data.txt
V1 V2 V3 V4 1 dat-131.2 2071.49 3.11E-302 2 dat-219.1 744.48 3.11E-302 3 dat-120.2 13.29 3.11E-302 4 dat-577.1 1.71 3.11E-302 5 dat-535.2 16.07 3.11E-302 6 dat-535.2 12.99 3.11E-302 7 dat-535.2 2147.31 3.11E-302 8 dat-535.2 61.21 3.11E-302 …
var_table.txt
dat-501.16 VarA dat-151.5 VarB dat-518.9 VarC dat-535.2 VarD dat-49.0 VarE
预期输出
生成如VarD.out的TSV文件,包含表头以及data.txt中V2列等于dat-535.2的所有整行:
V1 V2 V3 V4 5 dat-535.2 16.07 3.11E-302 6 dat-535.2 12.99 3.11E-302 7 dat-535.2 2147.31 3.11E-302 8 dat-535.2 61.21 3.11E-302
猜测问题出在变量赋值上,因为$2、$0对应data.txt的数据,而value1、value2来自var_table.txt。
解决方法
核心问题
脚本中awk代码用单引号包裹,单引号内的$value1不会被shell解析为外部变量,而是被当作字符串$value1去匹配data.txt的第二列,自然找不到匹配行。
修正后的脚本
用awk的-v参数将shell变量value1传递给awk内部变量,实现正确匹配:
while read -r value1 value2 do echo "$value2" awk -v target="$value1" -F "\t" 'NR==1 || $2 == target { print $0 }' data.txt > "${value2}".out done < "var_table.txt"
改动说明
- 传递变量:添加
-v target="$value1",把shell的value1值赋给awk的内部变量target - 合并条件:将
NR==1; $2 == target改为NR==1 || $2 == target,用逻辑或合并两个条件,逻辑更清晰(原分号写法也能输出表头,但合并后可读性更强) - 变量引用:在awk里直接用
target代替原来的"$value1",确保匹配的是实际的变量值
优化建议
如果data.txt文件很大,当前脚本会循环多次读取data.txt,效率较低。可以用一次awk处理完成所有匹配,提升性能:
awk -F "\t" ' NR==FNR { var_map[$1] = $2; next } FNR==1 { header = $0; next } { if ($2 in var_map) { if (!file_header[var_map[$2]]) { print header > var_map[$2] ".out" file_header[var_map[$2]] = 1 } print $0 > var_map[$2] ".out" } } ' var_table.txt data.txt
这个脚本仅读取两次文件(var_table.txt和data.txt各一次),先把变量映射关系存入数组,再遍历data.txt写入对应文件,适合大文件场景。
内容的提问来源于stack exchange,提问作者bactro
相关产品推荐
相关产品推荐

