2

I'm running a command which might face an error:

cmd sub-cmd --param1 value1 --param2 value2

There's a possibility that this command might return an error. The error message is one specific one, ending in a certain string (the beginning is different but the ending the always the same). I'm looking for a small shell script code to retry the same command in case cmd returns an error message ending in a specific string for a certain number of times (let's say 5 times). Also, please note that the cmd accepts parameters and I'm hoping that the shell script will pass whatever it gets to my cmd. And in case the error message was anything but the one I'm looking for, the shell script should return the error and halt (exit(1)). Thanks.

  • is the error message from cmd printed to stdout or to stderr? – Jeff Schaller Feb 6 at 16:04
  • I'm not sure but let's assume stderr – Mehran Feb 6 at 16:05
  • after writing that script below, I started to wonder if there would be a ready-made utility for this... – ilkkachu Feb 6 at 16:29
2

We'll need to capture the output of the command then. It's easiest if we can capture all of it (or just ignore the standard output):

#!/bin/bash
errormsg=$( cmd "$@" 2>&1 >/dev/null )
re="known error message$"
if [[ $errormsg =~ $re ]]; then
    echo "cmd failed with known message
fi

The command substitution $(..) and the redirections within it capture the standard error output of the command, and redirect the non-error output to /dev/null. Then we compare that against a regular expression, where the $ at the end signifies the end of the string. (The command substitution removes all trailing newlines, so the regex doesn't need to match those.) "$@" expands to the script's arguments, passing them as arguments to cmd.

Of course, we need a loop to repeat running the program. An alternative to command substitution would be to use a temporary file to store the error output and look into it afterwards. This makes it easier to have the standard output of the command visible, too, so let's do that:

#!/bin/bash
re="known error message$"

retries=0
max_retries=5
errorfile=$(mktemp)                       # temp file for the errors
trap 'rm "$errorfile"' EXIT               # remove the temp file on exit

while cmd "$@" 2> "$errorfile";           # run the command
      ret=$?;                             # save the exit code
      [[ $( < $errorfile ) =~ $re ]]; do  # check the output
    cat "$errorfile" >&2                  # print the errors to
                                          # the script's stderr, too

    retries=$((retries+1))                # retry a couple of times
    echo -n "cmd failed with known message"
    if [[ $retries -lt $max_retries ]]; then
        echo ", retrying."
    else
        echo ", exit."
        exit 1
    fi
done
cat "$errorfile" >&2
echo "cmd completed with status $ret";    # use saved exit code, since it might
exit "$ret"                               # have failed for other reasons

(while cmd1; cmd2; do ... runs cmd1 at the very start, then runs cmd2 as the loop condition.)

In the above, I ignored the actual exit status of the command and looked just at the output. You'll need to change the script if you want to take the exit status into account.

  • Thanks, is there any way I can keep getting the none error messages on the stdout? – Mehran Feb 6 at 16:17
  • @Mehran, yeah, I changed that to use a temp file – ilkkachu Feb 6 at 16:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.