KiloOne
KiloOne

Reputation: 334

My $? command succeeded code fails, why?

In my bash script, I am trying to have rsync retry 10 times if it looses connection to its destination before giving up.

I know this code traps all errors but I can live with that for now.

My code is:

lops="0"
while true; do
    let "lops++"
    rsync $OPT $SRCLINUX $TRG 2>&1 | tee --append ${LOGFILE}
    if [ "$?" -eq "0" ]; then
        echolog "rsync finished WITHOUT error after $lops times"
        break 
    else
        echolog "Re-starting rsync for the ${lops}th time due to ERRORS"
    fi
    if [[ "$lops" == "10" ]]; then
        echolog "GAVE UP DUE TO TOO MANY rsync ERRORS - BACKUP NOT FINISHED"
        break 
    fi
done

It does not work as expected, here is a what happens on the first error:

TDBG6/
rsync: connection unexpectedly closed (131505 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(226) [sender=3.1.1]
rsync finished WITHOUT error after 1 times

Is this because the $? contains the return value of tee, NOT rsync?

How can I fix this? (I am personally Linux syntax limited :)

Upvotes: 2

Views: 106

Answers (3)

Eric Renouf
Eric Renouf

Reputation: 14510

In addition to the other suggestions for working around the pipe confounding your exit code, you can avoid the pipe by using process substitution like so:

rsync "$OPT" "$SRCLINUX" "$TRG" &> >( tee --append "${LOGFILE}" )

which will redirect both stdout and stderr (that's the &> part) into a "file" that is connected to stdin of the process within the >(...), in this case the tee command you want. So it's very much like the pipe, but without the pipe (the pipe connects stdout of the left to stdin of the right behind the scenes, we've just pushed it out in the open here).

Upvotes: 2

gniourf_gniourf
gniourf_gniourf

Reputation: 46853

I see at least 2 possibilities to fix your problem:

  1. use PIPESTATUS:

    An array variable containing a list of exit status values from the processes in the most-recently-executed foreground pipeline (which may contain only a single command).

    Use it as:

    rsync $OPT $SRCLINUX $TRG 2>&1 | tee --append ${LOGFILE}
    if (( PIPESTATUS[0] == 0 )); then
    
  2. use rsync's --log-file option.

Notes:

  • you have lots of quotes missing in your code!
  • don't use uppercase variable names.
  • Don't use let and use ((...)) for arithmetic.

Upvotes: 3

Random832
Random832

Reputation: 39030

I believe that the status is the status of the last command in the pipeline.

I don't know how to deal with this in general, but for this specific case you can just redirect the output of the whole loop:

while true; do
    ...
done | tee -a "$LOGFILE"

Depending on what it's for, this may also mean you don't need the "echolog" function.

Upvotes: 1

Related Questions