Arithmetic syntax error with shell script

2019-09-05 01:36发布

问题:

I am trying to call the function "warn" if the calculation is TRUE. I am still not quite comfortable with the syntax, would like some tips on how to fix the last line.

    if [ "$noproc" -gt 0 ]; then
    echo "WARNING: NoProc at $noproc for $process processes." >> $log

        elif [ "$max" -ge 11 ]; then 
        [ $(($max - $total)) -lt 6 && [ $idle -le $(($max \* 0.25 | bc -l)) ] ] | warn $total $process $max $idle

The error I get: line 97: [: missing ` ] '

回答1:

If your tagging for this question is correct and you're genuinely using bash (which is to say that your script starts with #!/bin/bash, or if not started via a shebang you use bash yourscript rather than sh yourscript), you might as well take advantage of it.

# extended bash math syntax
if (( (max - total) < 6 )) && (( idle <= (max / 4) )); then
  warn "$total" "$process" "$max" "$idle"
fi

If, for whatever reason, you don't want to use (( )), you can still use [[ ]], which gives you a test context with its own extended syntax:

# extended bash test syntax
if [[ $((max - total)) -lt 6 && $idle -le $(bc -l <<<"$max*0.25") ]]; then
  warn "$total" "$process" "$max" "$idle"
fi

...whereas if you want to be compatible with POSIX sh, you need to end the test before you can put in a shell-level logical-AND operator.

# corrected POSIX-compliant test syntax
if [ "$((max - total))" -lt 6 ] && [ "$idle" -le "$(bc -l <<<"$max*0.25")" ]; then
  warn "$total" "$process" "$max" "$idle"
fi

To understand why, let's look at how your original command would parse, if you changed the (utterly incorrect) | symbol to && instead:

# Equivalent (longer form) of the original code, with pipe corrected to logical-AND
if [ $(($max - $total)) -lt 6; then
  if [ $idle -le $(($max \* 0.25 | bc -l)) ] ]; then
    warn $total $process $max $idle
  fi
fi

Note that this is running, as a single command, [ $(($max - $total)) -lt 6.

[ is not special shell syntax -- it's just a command. In older shells it was actually /usr/bin/[; today, there's also a [ builtin as well, but other than being faster to execute, it behaves exactly the same way as it would have were it executing the old, external command.

That [ command expects to be passed a ] as its last argument, since there's no ] after the -lt 6, you get a syntax error and it exits.

Similarly, your code would then (if the first command succeeded) run [ $idle -le $(($max \* 0.25 | bc -l)) ] ]. Here, you have a [ command passed two ]s on the end; it simply doesn't know what to do with the second one.



回答2:

  • You can't nest [ invocations. Even if you could, a && (b) === a && b in logic.
  • You can't use commands in arithmetic expansions.
  • Bash's [[ is safer than [.
  • Use More Quotes™.

Result:

[[ "$(($max - $total))" -lt 6 ]] && [[ "$idle" -le "$(bc -l <<< "$max * 0.25")" ]] | warn "$total $process $max $idle"


标签: bash shell unix