38

I would like to create a loop that repeats a ncftp transfer if it returns an error.

I'm a little unsure how the exit code variable can be used in a loop. Would something like this work?

until [$? == 0]; do
    ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
done
1

6 Answers 6

64

I found the basis for this elegant loop elsewhere on serverfault. Turns out there is no need save the exit code, as you can test directly on the command itself;

until ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file; do
  echo Tansfer disrupted, retrying in 10 seconds...
  sleep 10
done
1
  • 5
    Oneliner: until command; do :; done
    – pbies
    Dec 23, 2020 at 14:00
11

Almost. You are probably better saving the return value as a variable so that you can pre-set it before the loop. Otherwise it will be affected by the last-run command.

You might also want to sling a sleep in there to stop it respawning too quickly.

RET=1
until [ ${RET} -eq 0 ]; do
    ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
    RET=$?
    sleep 10
done
6
  • I think forgot to pass the exit code to the RET variable, but I get the idea. My main question is when will the until clause be evaluated? In C it won't be evaluated until after the first run of the loop, in which case my loop should work. Yours is still better, though, as it allows for intermediate commands such as the sleep. Thanks!
    – Roy
    Nov 3, 2009 at 9:33
  • Well spotted. I've edited it. The clause will be evaluated before the first run. So previously it would use the return code of any preceding command. The double-equals aren't enforced by Bash but it makes good practice to use.
    – Dan Carley
    Nov 3, 2009 at 9:51
  • == is for string equality not numeric, although I don't think it matters in this case... Nov 3, 2009 at 13:01
  • Single and double equals are the same. But it would be better to use integer equality now that you mention it.
    – Dan Carley
    Nov 3, 2009 at 13:33
  • 1
    You don't need to test the return code number -- you can just test the return code of the program itself -- until program ; do sleep 3 ; done .
    – chris
    Nov 3, 2009 at 22:15
6

Bit hacky but my solution was to just create a bash function which runs itself if it exits with a failure

function retry { the_command_you_want && echo "success" || (echo "fail" && retry) }; retry
3

Some of the solutions shown on this page will loop an infinite number of times if the command continues to fail.

If you're not a fan of infinite loops try this:

function retry {
    command="$*"
    retval=1
    attempt=1
    until [[ $retval -eq 0 ]] || [[ $attempt -gt 5 ]]; do
        # Execute inside of a subshell in case parent
        # script is running with "set -e"
        (
            set +e
            $command
        )
        retval=$?
        attempt=$(( $attempt + 1 ))
        if [[ $retval -ne 0 ]]; then
            # If there was an error wait 10 seconds
            sleep 10
        fi
    done
    if [[ $retval -ne 0 ]] && [[ $attempt -gt 5 ]]; then
        # Something is fubar, go ahead and exit
        exit $retval
    fi
}

Now, whenever you need a retry loop, just run:

retry mycommand and args

e.g.:

retry ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
2

You can do a loop while your command returns error:

    while [ -n $(ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file) ]; do
            sleep 1;
    done;
3
  • 3
    This answer needs explanation.
    – kasperd
    Dec 9, 2016 at 21:51
  • @kasperd explanation added Dec 12, 2016 at 15:59
  • this is the best way imo and I believe will work in POSIX (so /bin/sh) as well? Implementation may differ but I think syntax wise anyhow
    – BytePorter
    Jan 9 at 16:44
1

I've had a similar problem with needing to repeat curl requests, and the scripting was getting out of hand. As a result I created the retry tool:

retry ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file

In this case, the ncftpput command will be retried indefinitely until it returns a success status code, backing off by default for ten seconds in between.

Retry is available here: https://github.com/minfrin/retry

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .