#!/usr/bin/env bash GETOPT_BIN=$IN_GETOPT_BIN GETOPT_BIN=${GETOPT_BIN:-getopt} __sleep_amount() { if [ -n "$constant_sleep" ]; then sleep_time=$constant_sleep else #TODO: check for awk #TODO: check if user would rather use one of the other possible dependencies: python, ruby, bc, dc sleep_time=`awk "BEGIN {t = $min_sleep * $(( (1<<($attempts -1)) )); print (t > $max_sleep ? $max_sleep : t)}"` fi } __log_out() { echo "$1" 1>&2 } # Parameters: max_tries min_sleep max_sleep constant_sleep fail_script EXECUTION_COMMAND retry() { local max_tries="$1"; shift local min_sleep="$1"; shift local max_sleep="$1"; shift local constant_sleep="$1"; shift local fail_script="$1"; shift if [ -n "$VERBOSE" ]; then __log_out "Retry Parameters: max_tries=$max_tries min_sleep=$min_sleep max_sleep=$max_sleep constant_sleep=$constant_sleep" if [ -n "$fail_script" ]; then __log_out "Fail script: $fail_script"; fi __log_out "" __log_out "Execution Command: $*" __log_out "" fi local attempts=0 local return_code=1 while [[ $return_code -ne 0 && $attempts -le $max_tries ]]; do if [ $attempts -gt 0 ]; then __sleep_amount __log_out "Before retry #$attempts: sleeping $sleep_time seconds" sleep $sleep_time fi P="$1" for param in "${@:2}"; do P="$P '$param'"; done #TODO: replace single quotes in each arg with '"'"' ? export RETRY_ATTEMPT=$attempts bash -c "$P" return_code=$? #__log_out "Process returned $return_code on attempt $attempts" if [ $return_code -eq 127 ]; then # command not found exit $return_code elif [ $return_code -ne 0 ]; then attempts=$[$attempts +1] fi done if [ $attempts -gt $max_tries ]; then if [ -n "$fail_script" ]; then __log_out "Retries exhausted, running fail script" eval $fail_script else __log_out "Retries exhausted" fi fi exit $return_code } # If we're being sourced, don't worry about such things if [ "$BASH_SOURCE" == "$0" ]; then # Prints the help text help() { local retry=$(basename $0) cat < /dev/null if [[ $? -ne 4 ]]; then echo "I’m sorry, 'getopt --test' failed in this environment. Please load GNU getopt." exit 1 fi OPTIONS=vt:s:m:x:f: LONGOPTIONS=verbose,tries:,sleep:,min:,max:,fail: PARSED=$($GETOPT_BIN --options="$OPTIONS" --longoptions="$LONGOPTIONS" --name "$0" -- "$@") if [[ $? -ne 0 ]]; then # e.g. $? == 1 # then getopt has complained about wrong arguments to stdout exit 2 fi # read getopt’s output this way to handle the quoting right: eval set -- "$PARSED" max_tries=10 min_sleep=0.3 max_sleep=60.0 constant_sleep= fail_script= # now enjoy the options in order and nicely split until we see -- while true; do case "$1" in -v|--verbose) VERBOSE=true shift ;; -t|--tries) max_tries="$2" shift 2 ;; -s|--sleep) constant_sleep="$2" shift 2 ;; -m|--min) min_sleep="$2" shift 2 ;; -x|--max) max_sleep="$2" shift 2 ;; -f|--fail) fail_script="$2" shift 2 ;; --) shift break ;; *) echo "Programming error" exit 3 ;; esac done retry "$max_tries" "$min_sleep" "$max_sleep" "$constant_sleep" "$fail_script" "$@" fi