Bagaimana cara saya menulis logika coba lagi dalam skrip untuk terus mencoba lagi menjalankannya hingga 5 kali?

112

Saya ingin menulis logika dalam skrip shell yang akan mencoba lagi untuk menjalankannya lagi setelah 15 detik hingga 5 kali berdasarkan "kode status = GAGAL" jika gagal karena beberapa masalah.

Sandeep Singh
sumber

Jawaban:

90

Script ini menggunakan penghitung nuntuk membatasi upaya di perintah ke lima. Jika perintah berhasil, $?akan tahan nol dan eksekusi akan terputus dari loop.

n=0
until [ $n -ge 5 ]
do
   command && break  # substitute your command here
   n=$[$n+1]
   sleep 15
done
tersangka
sumber
1
Anda harus menambahkan breakjika perintah berhasil maka itu akan memutus loop
Rahul Patil
Sebenarnya cara yang tepat untuk menulis itu if command; then break; fiatau lebih command && break
tepatnya
1
"command" hanyalah nama perintah yang ingin Anda periksa statusnya.
tersangka
3
Patut dicatat bahwa Anda dapat menguji apakah n sama dengan lima pada akhirnya untuk mengetahui apakah perintah berhasil atau tidak.
mattdm
4
Solusi yang bagus - tetapi dalam kasus nkegagalan, tidak perlu tidur satu kali lagi sebelum keluar.
ron rothman
126
for i in 1 2 3 4 5; do command && break || sleep 15; done

Ganti "perintah" dengan perintah Anda. Ini dengan asumsi bahwa "kode status = GAGAL" berarti kode pengembalian yang bukan nol.


Variasi:

Menggunakan {..}sintaks. Bekerja di sebagian besar shell, tetapi tidak BusyBox sh:

for i in {1..5}; do command && break || sleep 15; done

Menggunakan seqdan meneruskan kode keluar dari perintah gagal:

for i in $(seq 1 5); do command && s=0 && break || s=$? && sleep 15; done; (exit $s)

Sama seperti di atas, tetapi melompati sleep 15setelah final gagal. Karena lebih baik hanya menetapkan jumlah loop maksimum satu kali, ini dicapai dengan tidur pada awal loop jika i > 1:

for i in $(seq 1 5); do [ $i -gt 1 ] && sleep 15; command && s=0 && break || s=$?; done; (exit $s)
Alexander
sumber
25
+1 - singkat dan jelas. Satu saran: Saya akan menggantinya for i in 1 2 3 4 5dengan for i in {1..5}karena lebih mudah dirawat.
Paddy Landau
5
Sekedar catatan, ini bekerja karena &&dievaluasi sebelum ||karena operator didahulukan
gene_wood
6
Catatan lain, ini akan mengembalikan kode 0 bahkan jika commandgagal.
Henrique Zambon
3
@ HenriqueZambon Menambahkan versi yang juga menangani itu.
Alexander
2
Bukankah ini tidur setelah kegagalan terakhir? Sepertinya menunggu 15-an yang tidak perlu. Saya pikir Anda dapat menempatkan cek [[ i -eq 5]]sebagai kondisi ATAU sebelum tidur untuk menghindari ini.
Dave Lugg
32
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
}

Contoh:

retry ping invalidserver

menghasilkan output ini:

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

Untuk dunia nyata, contoh kerja dengan perintah kompleks, lihat skrip ini .

Fernando Correia
sumber
3
Ini solusi hebat. Saya suka itu keluar dengan status keluar non-nol setelah sesuatu gagal beberapa kali juga.
Ben Liyanage
11

Ini adalah fungsi untuk coba lagi

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 $*

Keluaran:

[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 ::
Rahul Patil
sumber
Saya menyalin kode yang Anda tempel di file baru bernama retry.sh dan menambahkan baris #! / Bin / bash di bagian atas. Saat menjalankan dengan perintah yang Anda berikan dalam penjelasan saya tidak melihat apa-apa hanya prompt datang lagi.
java_enthu
Sudahkah Anda mencobabash retry.sh 3 ping -c1 localhost
Rahul Patil
Ya Rahul saya memang mencoba.
java_enthu
Maaf, saya aneh .., saya telah menguji lagi, itu berfungsi, periksa output paste.ubuntu.com/6002711
Rahul Patil
ini adalah jawaban paling elegan di sini sejauh ini - jika Anda melakukan sesuatu yang tidak sepele. Terima kasih telah meluangkan waktu.
Jerry Andrews
10

GNU Parallel memiliki --retries:

parallel --retries 5 --delay 15s ::: ./do_thing.sh
Ole Tange
sumber
5

Ini adalah alias / skrip satu baris favorit saya

    alias retry='while [ $? -ne 0 ] ; do fc -s ; done'

Maka Anda dapat melakukan hal-hal seperti:

     $ ps -ef | grep "Next Process"
     $ retry

dan itu akan terus menjalankan perintah sebelumnya sampai ia menemukan "Proses Selanjutnya"

Jeff
sumber
1
Di zsh, gunakan fc -e "#"sebagai ganti fc -s.
Ricardo Stuven
2

Saya menggunakan skrip ini yang membuat percobaan ulang dari perintah yang diberikan, manfaat dari skrip ini adalah bahwa jika gagal semua percobaan ulang akan mempertahankan kode keluar.

#!/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

Mungkin itu bisa menjadi lebih sederhana

padilo
sumber
Saya suka betapa fleksibelnya versi ini dan bagaimana verbose dan mudah dibaca kodenya!
yo.ian.g
Untuk mencocokkan gema yang gagal Anda bahkan dapat menambahkan gema yang berhasil dengan [$ ret_value -eq 0] atau menguji $ ret_value sesudahnya
yo.ian.g
Versi ini memiliki keuntungan tidak tidur setelah perintah gagal untuk terakhir kalinya.
Alexander
1

Lihat Contoh di bawah ini:

n=0
while :
do
        nc -vzw1 localhost 3859
        [[ $? = 0 ]] && break || ((n++))
        (( n >= 5 )) && break

done

Saya mencoba untuk menghubungkan port 3389 di localhost, itu akan mencoba lagi sampai gagal 5 kali, jika berhasil maka akan merusak loop.

$? itu ada status perintah jika nol berarti perintah berhasil dijalankan, jika selain nol berarti perintah fai

Sepertinya agak rumit, mungkin seseorang melakukannya lebih baik dari ini.

Rahul Patil
sumber
Terima kasih rahul .. apakah akan terus mencoba menjalankan skrip ??
Sandeep Singh
Silakan periksa sekarang, saya telah memperbarui
Rahul Patil
$?itu ada status perintah jika nol berarti perintah berhasil dijalankan, jika selain nol berarti perintah gagal
Rahul Patil
apakah harus memberikan alamat host dan port. bisakah kita melakukannya dengan memberikan dir lokasi skrip saja.
Sandeep Singh
ganti dengan perintah apa pun yang memberikan kode status keluar $?
Rahul Patil
1

Anda dapat menggunakan loopperintah, tersedia di sini , seperti:

$ loop './do_thing.sh' --every 15s --until-success --num 5 

Yang akan melakukan hal Anda setiap 15 detik hingga berhasil, maksimal lima kali.

Rich Jones
sumber
0

Berikut adalah retryfungsi rekursif untuk puritan pemrograman fungsional:

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
}

Berikan perintah (atau nama fungsi) dan secara opsional sejumlah percobaan dan durasi tidur antara percobaan ulang, seperti:

retry some_command_or_fn 5 15 # 5 tries, sleep 15 seconds between each
Mikhail Vasin
sumber
Ini tidak berfungsi untuk perintah yang panjangnya lebih dari satu kata: cmd = "echo blah blah" ... baris 10: [: bla: ekspresi integer diharapkan ... Juga tidak berfungsi untuk pipa, dll.
Mercury00