Làm cách nào để viết logic thử lại trong tập lệnh để tiếp tục thử lại để chạy nó tới 5 lần?


112

Tôi muốn viết logic trong shell script sẽ thử lại để chạy lại sau 15 giây tối đa 5 lần dựa trên "mã trạng thái = FAIL" nếu nó không thành công do một số vấn đề.

Câu trả lời:


90

Kịch bản lệnh này sử dụng một bộ đếm nđể giới hạn các lần thử tại lệnh thành năm. Nếu lệnh thành công, $?sẽ giữ số 0 và thực thi sẽ thoát khỏi vòng lặp.

n=0
until [ $n -ge 5 ]
do
   command && break  # substitute your command here
   n=$[$n+1]
   sleep 15
done

1
bạn nên thêm breaknếu lệnh thành công thì nó sẽ phá vỡ vòng lặp
Rahul Patil

Trên thực tế, cách viết đúng đắn if command; then break; fihay ngắn gọn hơncommand && break
tree 11/11/13

1
"Lệnh" chỉ là tên của lệnh mà bạn muốn kiểm tra trạng thái của.
suspectus

3
Đáng lưu ý rằng bạn có thể kiểm tra nếu n bằng năm ở cuối để biết lệnh có thành công hay không.
mattdm

4
Giải pháp tốt đẹp - nhưng trong trường hợp nthất bại, nó không cần ngủ thêm một lần nữa trước khi thoát ra.
ron rothman

126
for i in 1 2 3 4 5; do command && break || sleep 15; done

Thay thế "lệnh" bằng lệnh của bạn. Điều này giả định rằng "mã trạng thái = FAIL" có nghĩa là bất kỳ mã trả về khác không.


Biến thể:

Sử dụng {..}cú pháp. Hoạt động trong hầu hết các shell, nhưng không phải BusyBox sh:

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

Sử dụng seqvà chuyển dọc theo mã thoát của lệnh thất bại:

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

Tương tự như trên, nhưng bỏ qua sleep 15sau thất bại cuối cùng. Vì tốt hơn là chỉ xác định số vòng lặp tối đa một lần, điều này đạt được bằng cách ngủ ở đầu vòng lặp nếu i > 1:

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

25
+1 - cô đọng và rõ ràng. Một gợi ý: Tôi sẽ thay thế for i in 1 2 3 4 5với for i in {1..5}vì nó dễ dàng hơn để duy trì.
Paddy Landau

5
Chỉ cần một lưu ý, điều này hoạt động vì &&được đánh giá trước ||ưu tiên toán tử
gen_wood

6
Một lưu ý khác, điều này sẽ trả về mã 0 ngay cả khi commandthất bại.
Henrique Zambon

3
@HenriqueZambon Đã thêm một phiên bản cũng xử lý việc đó.
Alexander

2
Không ngủ này sau thất bại cuối cùng? Có vẻ như chờ đợi 15s không cần thiết. Tôi nghĩ rằng bạn có thể kiểm tra [[ i -eq 5]]như một điều kiện HOẶC trước khi ngủ để tránh điều này.
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
}

Thí dụ:

retry ping invalidserver

tạo đầu ra này:

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

Đối với một ví dụ thực tế, làm việc với các lệnh phức tạp, hãy xem tập lệnh này .


3
Đây là một giải pháp tuyệt vời. Tôi thích rằng nó thoát với trạng thái thoát khác không sau khi một cái gì đó đã thất bại nhiều lần là tốt.
Ben Liyanage

11

Đây là chức năng để thử lại

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

Đầu ra:

[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 ::

Tôi sao chép đã dán mã của bạn trong một tệp mới gọi là retry.sh và thêm một dòng #! / Bin / bash ở trên cùng. Trong khi chạy với các lệnh đã cho của bạn trong phần giải thích, tôi không thấy bất cứ điều gì chỉ cần nhắc lại.
java_enthu

bạn đã thử chưabash retry.sh 3 ping -c1 localhost
Rahul Patil

Vâng, tôi đã thử.
java_enthu

Xin lỗi, tôi đã rất kỳ quái .., tôi đã thử nghiệm lại, nó đang hoạt động, kiểm tra đầu ra dán.ubfox.com/6002711
Rahul Patil

đây là câu trả lời tao nhã nhất ở đây cho đến nay - nếu bạn đang làm một việc gì đó không tầm thường. Cảm ơn đã dành thời gian.
Jerry Andrew

10

GNU Song song có --retries:

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

5

Đây là bí danh / kịch bản yêu thích của tôi

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

Sau đó, bạn có thể làm những thứ như:

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

và nó sẽ tiếp tục chạy lệnh trước cho đến khi tìm thấy "Quá trình tiếp theo"


1
Trong zsh, sử dụng fc -e "#"thay vì fc -s.
Ricardo Stuven

2

Tôi sử dụng tập lệnh này để thực hiện thử lại một lệnh đã cho, lợi ích của tập lệnh này là nếu thất bại tất cả các lần thử lại, nó sẽ giữ nguyên mã thoát.

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

Có lẽ nó có thể đơn giản hơn


Tôi thích phiên bản này linh hoạt như thế nào và mã dài và dễ đọc như thế nào!
yo.ian.g

Để khớp với tiếng vang thất bại, bạn thậm chí có thể thêm tiếng vang thành công với [$ ret_value -eq 0] hoặc kiểm tra $ ret_value sau đó
yo.ian.g

Phiên bản này có ưu điểm là không ngủ sau khi lệnh thất bại lần cuối.
Alexander

1

Xem ví dụ dưới đây:

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

done

Tôi đang cố gắng kết nối cổng 3389 trên localhost, nó sẽ thử lại cho đến khi thất bại 5 lần, nếu thành công thì nó sẽ phá vỡ vòng lặp.

$? Nó tồn tại trạng thái của lệnh nếu nó không có nghĩa là lệnh chạy thành công, nếu khác không có nghĩa là lệnh fai

Có vẻ hơi phức tạp, có thể ai đó làm điều đó tốt hơn thế này.


Cảm ơn rahul .. nó sẽ được thử lại để chạy tập lệnh chứ ??
Sandeep Singh

Vui lòng kiểm tra ngay bây giờ, tôi đã cập nhật
Rahul Patil

$?Nó tồn tại trạng thái của lệnh nếu nó bằng 0 có nghĩa là lệnh chạy thành công, nếu khác không có nghĩa là lệnh thất bại
Rahul Patil

được yêu cầu cung cấp địa chỉ máy chủ và cổng. chúng ta có thể làm điều đó bằng cách chỉ đưa ra vị trí script script.
Sandeep Singh

thay thế bằng bất kỳ lệnh nào cung cấp mã trạng thái thoát $?
Rahul Patil

1

Bạn có thể sử dụng looplệnh, có sẵn ở đây , như vậy:

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

Điều này sẽ làm việc của bạn cứ sau 15 giây cho đến khi thành công, tối đa năm lần.


0

Đây là một retryhàm đệ quy cho những người theo chủ nghĩa lập trình chức năng:

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
}

Truyền cho nó một lệnh (hoặc tên hàm) và tùy chọn một số lần thử lại và thời gian ngủ giữa các lần thử lại, như vậy:

retry some_command_or_fn 5 15 # 5 tries, sleep 15 seconds between each

Điều này không hoạt động đối với các lệnh dài hơn một từ: cmd = "echo blah blah" ... dòng 10: [: blah: biểu thức số nguyên dự kiến ​​... Nó cũng không hoạt động cho các đường ống, v.v.
Mercury00
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.