Chcę napisać logikę w skrypcie powłoki, który spróbuje uruchomić go ponownie po 15 sekundach do 5 razy w oparciu o „kod statusu = FAIL”, jeśli nie powiedzie się z powodu jakiegoś problemu.
Chcę napisać logikę w skrypcie powłoki, który spróbuje uruchomić go ponownie po 15 sekundach do 5 razy w oparciu o „kod statusu = FAIL”, jeśli nie powiedzie się z powodu jakiegoś problemu.
Odpowiedzi:
Ten skrypt używa licznika, n
aby ograniczyć liczbę prób wykonania polecenia do pięciu. Jeśli polecenie zakończy się powodzeniem, $?
utrzyma zero i wykonanie przerwie się w pętli.
n=0
until [ $n -ge 5 ]
do
command && break # substitute your command here
n=$[$n+1]
sleep 15
done
if command; then break; fi
a ściślej command && break
n
awarii niepotrzebnie śpi jeszcze jeden raz przed wyjściem.
for i in 1 2 3 4 5; do command && break || sleep 15; done
Zamień „polecenie” na swoje polecenie. Zakłada się, że „kod statusu = FAIL” oznacza dowolny niezerowy kod powrotu.
Korzystanie ze {..}
składni. Działa w większości powłok, ale nie BusyBox sh
:
for i in {1..5}; do command && break || sleep 15; done
Używanie seq
i przekazywanie kodu wyjścia nieudanego polecenia:
for i in $(seq 1 5); do command && s=0 && break || s=$? && sleep 15; done; (exit $s)
To samo co powyżej, ale pomijanie sleep 15
po ostatecznym niepowodzeniu. Ponieważ lepiej jest zdefiniować maksymalną liczbę pętli tylko raz, osiąga się to poprzez uśpienie na początku pętli, jeśli i > 1
:
for i in $(seq 1 5); do [ $i -gt 1 ] && sleep 15; command && s=0 && break || s=$?; done; (exit $s)
for i in 1 2 3 4 5
ją, for i in {1..5}
ponieważ jest łatwiejsza w utrzymaniu.
&&
jest oceniana przed ze ||
względu na pierwszeństwo operatora
command
nie powiedzie.
[[ i -eq 5]]
przed snem, aby tego uniknąć.
function fail {
echo $1 >&2
exit 1
}
function retry {
local n=1
local max=5
local delay=15
while true; do
"$@" && break || {
if [[ $n -lt $max ]]; then
((n++))
echo "Command failed. Attempt $n/$max:"
sleep $delay;
else
fail "The command has failed after $n attempts."
fi
}
done
}
Przykład:
retry ping invalidserver
tworzy ten wynik:
ping: unknown host invalidserver
Command failed. Attempt 2/5:
ping: unknown host invalidserver
Command failed. Attempt 3/5:
ping: unknown host invalidserver
Command failed. Attempt 4/5:
ping: unknown host invalidserver
Command failed. Attempt 5/5:
ping: unknown host invalidserver
The command 'ping invalidserver' failed after 5 attempts
Aby zobaczyć prawdziwy przykład pracy ze złożonymi poleceniami, zobacz ten skrypt .
Oto funkcja ponownej próby
function retry()
{
local n=0
local try=$1
local cmd="${@: 2}"
[[ $# -le 1 ]] && {
echo "Usage $0 <retry_number> <Command>"; }
until [[ $n -ge $try ]]
do
$cmd && break || {
echo "Command Fail.."
((n++))
echo "retry $n ::"
sleep 1;
}
done
}
retry $*
Wynik :
[test@Nagios ~]$ ./retry.sh 3 ping -c1 localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.207 ms
--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.207/0.207/0.207/0.000 ms
[test@Nagios ~]$ ./retry.sh 3 ping -c1 localhostlasjflasd
ping: unknown host localhostlasjflasd
Command Fail..
retry 1 ::
ping: unknown host localhostlasjflasd
Command Fail..
retry 2 ::
ping: unknown host localhostlasjflasd
Command Fail..
retry 3 ::
bash retry.sh 3 ping -c1 localhost
Oto mój ulubiony alias / skrypt w jednym wierszu
alias retry='while [ $? -ne 0 ] ; do fc -s ; done'
Następnie możesz robić takie rzeczy jak:
$ ps -ef | grep "Next Process"
$ retry
i będzie uruchamiał poprzednie polecenie, aż znajdzie „Następny proces”
fc -e "#"
zamiast fc -s
.
Używam tego skryptu, który wykonuje ponowną komendę, zaletą tego skryptu jest to, że jeśli nie powiedzie się wszystkie próby, zachowa kod wyjścia.
#!/usr/bin/env bash
if [ $# -ne 3 ]; then
echo 'usage: retry <num retries> <wait retry secs> "<command>"'
exit 1
fi
retries=$1
wait_retry=$2
command=$3
for i in `seq 1 $retries`; do
echo "$command"
$command
ret_value=$?
[ $ret_value -eq 0 ] && break
echo "> failed with $ret_value, waiting to retry..."
sleep $wait_retry
done
exit $ret_value
Prawdopodobnie może być łatwiej
Zobacz poniżej Przykład:
n=0
while :
do
nc -vzw1 localhost 3859
[[ $? = 0 ]] && break || ((n++))
(( n >= 5 )) && break
done
Usiłuję podłączyć port 3389 na localhost, będzie ponawiał próbę aż do 5 razy nie powiedzie się, jeśli sukces zakończy się przerwaniem pętli.
$?
istnieje status polecenia, jeśli zero oznacza, że polecenie zostało pomyślnie uruchomione, jeśli inne niż zero oznacza polecenie fai
Wydaje się to trochę skomplikowane, być może ktoś zrobi to lepiej niż to.
$?
to istnieje status polecenia jeśli zero oznacza pomyślnie uruchomić polecenie, jeśli inna niż zero oznacza komenda nie
Możesz użyć loop
polecenia dostępnego tutaj :
$ loop './do_thing.sh' --every 15s --until-success --num 5
Co zrobi twoje zadanie co 15 sekund, aż się powiedzie, maksymalnie pięć razy.
Oto retry
funkcja rekurencyjna dla funkcjonalnych programistów purystów:
retry() {
cmd=$1
try=${2:-15} # 15 by default
sleep_time=${3:-3} # 3 seconds by default
# Show help if a command to retry is not specified.
[ -z "$1" ] && echo 'Usage: retry cmd [try=15 sleep_time=3]' && return 1
# The unsuccessful recursion termination condition (if no retries left)
[ $try -lt 1 ] && echo 'All retries failed.' && return 1
# The successful recursion termination condition (if the function succeeded)
$cmd && return 0
echo "Execution of '$cmd' failed."
# Inform that all is not lost if at least one more retry is available.
# $attempts include current try, so tries left is $attempts-1.
if [ $((try-1)) -gt 0 ]; then
echo "There are still $((try-1)) retrie(s) left."
echo "Waiting for $sleep_time seconds..." && sleep $sleep_time
fi
# Recurse
retry $cmd $((try-1)) $sleep_time
}
Przekaż komendę (lub nazwę funkcji) i opcjonalnie liczbę ponownych prób oraz czas oczekiwania między kolejnymi próbami:
retry some_command_or_fn 5 15 # 5 tries, sleep 15 seconds between each
break
że jeśli komenda się powiedzie, to przerwie to pętlę