Repeatedly run a shell command until it fails?

Bash

Bash Problem Overview


I've written a fuzzy test that fails unreliably. I've added some debug code, but now I want to run the test until it fails so I can gather the debug output.

I've setup the test so I can run it using:

./runtest

My current solution is to write an untilfail script:

#!/bin/bash
$@
while [ $? -eq 0 ]; do
    $@
done

Then use it:

untilfail ./runtest

Is there a simpler solution?

Bash Solutions


Solution 1 - Bash

while takes a command to execute, so you can use the simpler

while ./runtest; do :; done

This will stop the loop when ./runtest returns a nonzero exit code (which is usually indicative of failure).

To further simplify your current solution though, you should just change your untilfail script to look like this:

#!/bin/bash

while "$@"; do :; done

And then you can call it with whatever command you're already using:

untilfail ./runTest --and val1,val2 -o option1 "argument two"

Solution 2 - Bash

If you don't want to wrap a complex pipe line into a shell script or function then this works:

while true; do 
  curl -s "https:..." | grep "HasErrors.:true"
  if [[ "$?" -ne 0 ]]; then 
    break
  fi
  sleep 120
done

The HTTP request in this case always returns 200 but also returns some JSON which has an attribute "HasErrors":true when there is an error.

Solution 3 - Bash

Having had a similar problem in a system that had shell retry logic duplicated everywhere I made a dedicated tool to solve this called "retry":

retry --until=fail ./runtest

A more complex example:

retry --until=fail --message="test succeeded" --delay=1 ./runtest

Tool available from https://github.com/minfrin/retry.

Solution 4 - Bash

On Ubuntu:

sudo apt-get install run-one
run-one-until-failure COMMAND [ARGS]

For more information: https://blog.dustinkirkland.com/2013/09/introducing-run-one-constantly-run-one.html

Solution 5 - Bash

One can also use the watch command with the -e flag which runs the command until an error occurs (and then freezes the output until a key is pressed):

watch -e ./runtest

The only downside of using watch is that there's a mandatory delay of at least 0.1s between runs:

watch -e ./runtest -n0.1

Witch watch you can also highlight output changes between runs (-d) or exit when the output of the command changes (-g).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionbradley.ayersView Question on Stackoverflow
Solution 1 - BashnneonneoView Answer on Stackoverflow
Solution 2 - BashJudd RogersView Answer on Stackoverflow
Solution 3 - BashGraham LeggettView Answer on Stackoverflow
Solution 4 - BashReunanenView Answer on Stackoverflow
Solution 5 - BashPankratView Answer on Stackoverflow