首页 > 解决方案 > for 循环内的 AWK 条件状态返回比预期更多的结果

问题描述

大家好,我正在尝试制作一个矩阵,如果它满足条件则打印一个元素,如果它不满足它,则打印一个字符。文件如下:names.txt

name1
name2
name3
name4
name5
name6
name7

nthits.txt

name1 1 hits found
name2 2 hits found
name3 2 hits found
name4 4 hits found
name5 4 hits found
name6 2 hits found
name7 2 hits found

phits.txt

name7 1 hits found

我使用的代码是:

NAMES=$(cat names.txt)
for NAME in $NAMES; do
    HITS_NT=$(awk -F" " -v NAME=$NAME '{if ($1 == NAME) {print $2} else {print "0"}}' nthits.txt)
    HITS_AA=$(awk -F" " -v NAME=$NAME '{if ($1 == NAME) {print $2} else {print "0"}}' phits.txt)
    echo -e $NAME"\t"$HITS_NT"\t"$HITS_AA
done

预期的结果是这样

name1   1   0
name2   2   0
name3   2   0
name4   4   0
name5   4   0
name6   2   0
name7   2   1

但是在执行它时,它会打印出比预期更多的字符。

name1   1 0 0 0 0 0 0   0
name2   0 2 0 0 0 0 0   0
name3   0 0 2 0 0 0 0   0
name4   0 0 0 4 0 0 0   0
name5   0 0 0 0 4 0 0   0
name6   0 0 0 0 0 2 0   0
name7   0 0 0 0 0 0 2   1

有什么建议或建议吗??非常感谢。

标签: bashmatrixawk

解决方案


首先,只需一次awk调用即可。

$: awk 'FILENAME == "names.txt" { print $0 "\t" lookup["nthits.txt"$0]+0 "\t" lookup["phits.txt"$0]+0 }
        { lookup[ FILENAME $1 ]=$2 }' nthits.txt phits.txt names.txt
name1   1   0
name2   2   0
name3   2   0
name4   4   0
name5   4   0
name6   2   0
name7   2   1

其次,尽量避免使用for循环读取文件。这个构造:

NAMES=$(cat names.txt)
for NAME in $NAMES; do

容易出错。如果使用bash,请尝试以下操作:

while read -r name; do
  # . . . 
done < names.txt

awk是更好的工具,但是要在纯脚本中执行相同的操作,请完全bash 消除awk,因为重复的子进程生成实际上会减慢它的速度。

declare -A nthits phits
while read -r name hits _; do nthits[$name]=$hits; done < nthits.txt
while read -r name hits _; do phits[$name]=$hits;  done < phits.txt
while read -r name; do
  printf "%s\t%s\t%s\n" "$name" "${nthits[$name]:=0}" "${phits[$name]:=0}"
done < names.txt

输出中的额外字段是因为您对每一行都进行了多重处理。您正在遍历 names.txt 中的每个名称,并为每个名称创建 nthits.txt 和 phits.txt 的每一行的输出。随着这些文件变大(更多记录),您应该注意到输出中列的相应增长。要执行您尝试执行的操作,您应该在每次调用 awk 时仅选择循环中的当前名称。

为此,您应该使用

awk -v name=$NAME '$1 == name {print $2}' nthits.txt

它仍然会读取每一行,但不会为不匹配的那些生成输出。如果没有匹配项,这仍然不会产生 0,所以你需要类似的东西

awk -v name=$NAME 'BEGIN{ out=0 } $1 == name { out=$2 } END{ print out }' nthits.txt

而且您仍然必须打印名称和选项卡...尽管如此
,更容易awk在顶部执行所有操作。:)


推荐阅读