Ubuntu – AWK: why does $(cat) work for stdin, but $* doesn’t

awkbash

echo '((3+(2^3)) * 34^2 / 9)-75.89' | awk "BEGIN{ print $(cat) }"

The above syntax works fine with the calculated result '1337'.

echo '((3+(2^3)) * 34^2 / 9)-75.89' | awk "BEGIN{ print $* }"

But the above syntax doesn't work, though there's no error.

Plz advise.

Best Answer

The $(command) syntax will return the output of command. Here, you are using the very simple cat program whose only job is to copy everything from standard input (stdin) into standard output (stdout). Since you are running the awk script inside double quotes, the $(cat) is expanded by the shell before the awk script is run, so it reads the echo output into its stdin and duly copies it to its stdout. This is then passed to the awk script. You can see this in action with set -x:

$ set -x
$ echo '((3+(2^3)) * 34^2 / 9)-75.89' | awk "BEGIN{ print $(cat) }"
+ echo '((3+(2^3)) * 34^2 / 9)-75.89'
++ cat
+ awk 'BEGIN{ print ((3+(2^3)) * 34^2 / 9)-75.89 }'
1337

So, awk is actually running BEGIN{ print ((3+(2^3)) * 34^2 / 9)-75.89 }' which returns 1337.

Now, the $* is a special shell variable that expands to all the positional parameters given to a shell script (see man bash):

   *      Expands to the positional parameters, starting from one.  When the expan‐
          sion  is not within double quotes, each positional parameter expands to a
          separate word.  In contexts where it is performed, those words  are  sub‐
          ject  to  further word splitting and pathname expansion.  When the expan‐
          sion occurs within double quotes, it expands to a single  word  with  the
          value  of each parameter separated by the first character of the IFS spe‐
          cial variable.  That is, "$*" is equivalent to "$1c$2c...",  where  c  is
          the  first  character of the value of the IFS variable.  If IFS is unset,
          the parameters are separated by spaces.  If IFS is null,  the  parameters
          are joined without intervening separators.

However, this variable is empty here. Therefore, the awk script becomes:

$ echo '((3+(2^3)) * 34^2 / 9)-75.89' | awk "BEGIN{ print $* }"
+ awk 'BEGIN{ print  }'
+ echo '((3+(2^3)) * 34^2 / 9)-75.89'

The $* expands to an empty string, and awk is told to print an empty string, and this is why you get no output.


You might want to just use bc instead:

$ echo '((3+(2^3)) * 34^2 / 9)-75.89' | bc
1336.11
Related Question