首页 > 解决方案 > 分配和存储文件总行数的变量,以在 shell & awk 脚本中的下一个命令中使用

问题描述

请让我知道以下命令中的错误

for i in file*; do printf "$i processing" && \
for j in {3..10}; do printf ">=$j\t" && k=$(< "$i" wc -l) ; \
awk 'BEGIN {FS="\t"} {if ($7 >= '$j') {print $0} }' $i| \
awk 'END{print (NR/"$k")*100}'  ; done ; done

file1 processong
>=3 awk: cmd. line:1: (FILENAME=- FNR=126052) fatal: division by zero attempted
>=4 awk: cmd. line:1: (FILENAME=- FNR=118562) fatal: division by zero attempted
>=5 awk: cmd. line:1: (FILENAME=- FNR=113376) fatal: division by zero attempted
>=6 awk: cmd. line:1: (FILENAME=- FNR=109501) fatal: division by zero attempted
>=7 awk: cmd. line:1: (FILENAME=- FNR=106388) fatal: division by zero attempted
>=8 awk: cmd. line:1: (FILENAME=- FNR=103781) fatal: division by zero attempted
>=9 awk: cmd. line:1: (FILENAME=- FNR=101547) fatal: division by zero attempted
>=10    awk: cmd. line:1: (FILENAME=- FNR=99552) fatal: division by zero attempted

谢谢

标签: shellawk

解决方案


您获得除以零的原因是由于以下行:

 awk 'END{print (NR/"$k")*100}'

您尝试使用$k您分配为的 shell 变量k=$(< "$i" wc -l)。但是,您在单引号字符串中使用它,因此 bash 不会像您想象的那样执行变量替换。它只在双引号字符串中执行此操作。更多细节在这里:如何在 awk 脚本中使用 shell 变量?

所以你的神秘线应该是这样的:

for i in file*; do
  printf "$i processing" &&                                    \
    for j in {3..10}; do
      printf ">=$j\t" && k=$(< "$i" wc -l)
      awk -v j="$j" 'BEGIN {FS="\t"} {if ($7 >= j) {print $0} }' $i   \
        | awk -v k="$k" 'END{print (NR/k)*100}'
    done
done

但是,这个命令可以清理很多:

  • 不需要使用 &&. (printf不会失败)
  • 不要使用printf "string",而是printf -- "%s" "string"
  • 使用时awk,您不需要任何形式,如grep, wc, sed, ...
for i in file*; do
  printf -- "%s" "$i processing"
  for j in {3..10}; do
    printf -- "%s" ">=$j\t"
    awk -v j="$j" 'BEGIN {FS="\t"}($7>j) { print; c++ }END{print (c/NR)*100}' "$i"
  done
done

使用 GNU awk,您甚至可以将整个代码块缩减为单个 awk,但这超出了本问题的范围。


推荐阅读