0

Please let me know the mistake in the following command

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

Thanks

Nagesh S.
  • 3
  • 2
  • Welcome to SO, thanks for showing your efforts in form of code. Could you please do add sample of input and expected output to give us clear picture of your question. Kindly do edit your question and let us know then. – RavinderSingh13 Jun 09 '20 at 05:00
  • 1
    `"$i" wc -l` should be `wc -l "$i"` You are trying to get count of records in file right – Digvijay S Jun 09 '20 at 05:17
  • Also you need to fix your code in terms of how you are sending shell variables into `awk`. you need to use `-v var="$shell_variable"` to send shell variables to `wk` as a variable too. – RavinderSingh13 Jun 09 '20 at 05:26
  • 2
    @DigvijayS : I think you mean `wc -l <"$i"`, because we need only the number, not the file name with it. This is incidentally the same as `< "$i" wc -l`, as the OP has written (he just used a unusual order, but it's not wrong). – user1934428 Jun 09 '20 at 07:29

1 Answers1

2

The reason you obtain division by zero is due to the following line:

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

You attempt to use a shell variable $k which you assigned as k=$(< "$i" wc -l). However, you use it in a single quoted string, so bash does not perform the variable substitution as you imagine. It only does this in double-quoted strings. More details here: How do I use shell variables in an awk script?

So your mystery line should look like:

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

However, this command can be cleaned up a lot:

  • no need for compound commands using &&. (printf will not fail)
  • Don't use printf "string", but rather printf -- "%s" "string"
  • when using awk, you are not in need of anything of the form like 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

Using GNU awk, you can even reduce this entire code-block into a single-awk, but this is beyond the scope of this question.

kvantour
  • 25,269
  • 4
  • 47
  • 72