Bash Script: Retry FTP Transfer on Failure Using Exit Status and Loop


2 views

When automating file transfers via ncftp in bash scripts, network instability or temporary server issues can cause failures. Unlike human operators who retry manually, scripts need explicit error handling.

The $? variable stores the exit code of the last executed command:

ncftpput -DD -z -u user -p password server /remote /local/file
echo $? # Prints 0 for success, non-zero for failure

The original approach has syntax issues. Here's a corrected version:

until [ $? -eq 0 ]; do
    ncftpput -DD -z -u user -p password server /remote /local/file
done

Basic loops risk infinite retries. Add these improvements:

max_retries=5
retry_count=0

until [ $? -eq 0 ] || [ $retry_count -ge $max_retries ]; do
    ((retry_count++))
    ncftpput -DD -z -u user -p password server /remote /local/file
    
    if [ $? -ne 0 ]; then
        echo "Attempt $retry_count failed, retrying in 5 seconds..."
        sleep 5
    fi
done

[ $retry_count -ge $max_retries ] && echo "Max retries reached" >&2

For production systems, implement progressive delays between retries:

max_retries=8
base_delay=2
max_delay=300

for ((i=1; i<=max_retries; i++)); do
    ncftpput -DD -z -u user -p password server /remote /local/file
    
    if [ $? -eq 0 ]; then
        echo "Transfer succeeded"
        break
    fi
    
    delay=$((base_delay ** i))
    [ $delay -gt $max_delay ] && delay=$max_delay
    
    echo "Attempt $i failed, retrying in $delay seconds..."
    sleep $delay
done

For reusable code across scripts:

retry_command() {
    local cmd="$@"
    local max_attempts=3
    local attempt=1
    
    until $cmd || [ $attempt -ge $max_attempts ]; do
        echo "Command failed, retrying..."
        ((attempt++))
        sleep 1
    done
    
    return $?
}

retry_command ncftpput -DD -z -u user -p password server /remote /local/file

Differentiate between temporary and permanent failures:

ncftpput -DD -z -u user -p password server /remote /local/file
exit_code=$?

case $exit_code in
    0) echo "Success" ;;
    1) echo "Usage error - not retryable" >&2 ;;
    2) echo "Connection failed - will retry" ;;
    *) echo "Unknown error $exit_code" >&2 ;;
esac

When automating file transfers with ncftp, network hiccups or temporary server issues can cause failures. Instead of manually restarting failed transfers, we can create a robust bash script that automatically retries until success.

In bash, every command returns an exit status code. A value of 0 indicates success, while non-zero means failure. We can leverage this in our retry logic.

Here's a simple implementation that keeps retrying until the transfer succeeds:

#!/bin/bash

until ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
do
    echo "Transfer failed, retrying in 5 seconds..."
    sleep 5
done

For production use, we should add several improvements:

#!/bin/bash

MAX_RETRIES=10
RETRY_DELAY=30
RETRY_COUNT=0

until [ $RETRY_COUNT -eq $MAX_RETRIES ] || ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
do
    ((RETRY_COUNT++))
    echo "Attempt $RETRY_COUNT failed, waiting $RETRY_DELAY seconds..."
    sleep $RETRY_DELAY
done

if [ $RETRY_COUNT -eq $MAX_RETRIES ]
then
    echo "Transfer failed after $MAX_RETRIES attempts."
    exit 1
else
    echo "Transfer completed successfully."
    exit 0
fi

We can make the script more sophisticated by checking specific error codes:

#!/bin/bash

MAX_RETRIES=5
RETRY_DELAY=10

for ((i=1; i<=MAX_RETRIES; i++))
do
    ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
    EXIT_CODE=$?
    
    case $EXIT_CODE in
        0)  echo "Transfer successful"
            exit 0
            ;;
        1)  echo "Local file error"
            exit 1
            ;;
        2)  echo "Connection error (retryable)"
            sleep $RETRY_DELAY
            continue
            ;;
        3)  echo "Authentication error"
            exit 1
            ;;
        *)  echo "Unknown error (retryable)"
            sleep $RETRY_DELAY
            continue
            ;;
    esac
done

echo "Maximum retries reached"
exit 1

For better handling of persistent issues, we can implement exponential backoff:

#!/bin/bash

MAX_RETRIES=8
INITIAL_DELAY=5
BACKOFF_FACTOR=2

DELAY=$INITIAL_DELAY

for ((i=1; i<=MAX_RETRIES; i++))
do
    echo "Attempt $i of $MAX_RETRIES..."
    ncftpput -DD -z -u user -p password remoteserver /remote/dir /local/file
    
    if [ $? -eq 0 ]
    then
        echo "Transfer successful"
        exit 0
    fi
    
    echo "Waiting $DELAY seconds before retry..."
    sleep $DELAY
    DELAY=$((DELAY * BACKOFF_FACTOR))
done

echo "Transfer failed after $MAX_RETRIES attempts"
exit 1