Pure.Krome
Pure.Krome

Reputation: 86957

A shell command to run 'x' number of times while a command keeps failing

I'm trying to run a shell command (currently either sh or bash) which connects to a database. Because the database is still 'warming up', the command fails.

So I was trying to do a loop (let's say ... 100 tries) and each time the command fails, wait 1 second and retry.

If there's an error, this is the start of the string that is dumped to stdout: Sqlcmd: Error: <snipped>

Here's what I've been trying:


for i in $(seq 1 100)
do
    X='/opt/mssql-tools/bin/sqlcmd -E -S localhost -Q "<some sql statement> "'
    if [[ $X == Sqlcmd: Error:* ]]
        echo "."
    then
        break
    fi
done

It's not working as I figure out the string comparison stuff with shell/bash ... but was more making sure if I was on the right track etc.

Upvotes: 1

Views: 857

Answers (2)

Matthieu
Matthieu

Reputation: 3097

You could try something like:

while true ; do
    if Sqlcmd xxx xxx xxx ; then break ; fi
    # or:
    Sqlcmd xx xxx xxx && break
    sleep 1
done

You can also add a counter:

for ((n=100;n>0;n--)) ; do
    Sqlcmd xxx xxx xxx
    if [[ $? == 0 ]] ; then
        break
    fi
    sleep 1
done
[[ $n == 0 ]] && echo Timeout && exit 1

I'm showing two different ways of testing the return value here, but the first one is preferred (if cmd ; then ... ; fi).

$? is the return value from the last command, which is 0 when it completed successfully. If it returns 0 even in case of error (which can happen for malformed programs), you can test the output with grep:

Sqlcmd xxx xxx 2>&1 | grep <error pattern> > /dev/null
if [[ $? != 0 ]] ; then break ; fi

Here we test $? != 0 because grep will return 0 when the error pattern has been found.

If you want to get the output result into a variable, run the command with X=$(Sqlcmd xxx xxx). Then you can use bash string comparison:

X=$(Sqlcmd xxx xxx)
if [[ "$X" =~ .*error.* ]] ; then
    <handle error here>
fi

Note bash can match regexp, which makes it really handy at checking error types.

You can also use a switch/case construct:

case "$X" in
    *Error:*) echo " Error detected " ;;
    *) break ;;
esac

(Note the double ;;)

Upvotes: 4

Pure.Krome
Pure.Krome

Reputation: 86957

I ended up learning all the clues from @matthieu's post. This is what I ended up doing:

for i in $(seq 1 30)
do
    /opt/mssql-tools/bin/sqlcmd -U sa -P <snip> -S localhost -Q "USE Master" 2>&1

    if [[ $? != 0 ]]
    then
        # Failed
        echo "."
        sleep 1s
    else
        # worked!
        break
    fi
done

breakdown for those learning (like me)

  • execute a sql query using the sqlcmd command. Any errors via stderr (that's the 2 in 2>&1) will be redirected to the console stdout (that's the $1). REF: 2>&1 shell idiom.
  • the result status code is sent to $? (REF: what is bash dollar questionmark ?)
  • if it failed(any value that is NOT a zero), then sleep 1 sec and we'll try. Only re-try 30 times, though.
  • if we worked (value is zero), then stop trying and go on....

So there we have it! shell/bash shell 101 stuff. good luck!

Upvotes: 1

Related Questions