Search code examples
bashexitstatus

Is this script (to iterate over files, pass them as arguments to another script, count failures and sum successful outputs) correctly written?


I am working on an exam practice problem in which we need to invoke a script on a file. If the exit status is non-zero, we increment the number of fails by one, else we increment the sum variable by one. Since we don't actually have this script, I just wanted to verify that what I wrote on paper is correct, assuming that the script we are calling is called compute, and args are all the file arguments.

SUM=0
NUMFAILS=0
SCRIPT=./$compute

for args in *; do
    num=$SCRIPT args
    if (($? -ne 0)); then
        NUMFAILS++
    else
        SUM=(($SUM+$num))
    fi
done

Solution

  • sum=0
    numfails=0
    
    shopt -s nullglob
    
    for args in *; do
        if num=$(./compute "$args"); then
           ((sum+=num))
        else
           ((numfails++)) 
        fi
    done
    
    • You can use $? for testing exit status of the last command or you can test it with if directly: if command; then echo CMD OK; fi
    • You can assign output of a command to a variable while testing its exit code:
      if output=$(command); then echo CMD OK; fi
    • Do not use uppercase variables as they could collide with environment and internal shell variables
    • It is not wise to store commands in variables: BashFAQ #50
    • NUMFAILS++: you still need to use (( to evaluate the expression: ((numfails++))
    • num=$SCRIPT args: you need to use command substitution to substitute the output of a command: num=$(./script "$args")
    • args is a variable, you need to expand it with a dollar sign: "$args". Quotes are necessary to prevent word-splitting. Note that in arithmetic context, for example ((++numfails)), you don't need to use dollar sign
    • You might want to use shopt -s nullglob to skip the for loop if there are no files in your directory
    • On @CharlesDuffy's suggestion, If you are using set -e, you should use preincrement ((++numfails)) and ((sum+=num)) || true to handle cases where set -e would terminate the script when the result of either arithmetic expression is equal to 0
    • Use shellcheck