ฉันจะเขียนตรรกะลองใหม่ในสคริปต์เพื่อลองอีกครั้งเพื่อให้ทำงานได้ถึง 5 ครั้งได้อย่างไร


112

ฉันต้องการเขียนตรรกะในเชลล์สคริปต์ซึ่งจะลองอีกครั้งเพื่อให้ทำงานอีกครั้งหลังจาก 15 วินาทีนานถึง 5 ครั้งโดยอ้างอิงจาก"status code = FAIL"หากล้มเหลวเนื่องจากปัญหาบางอย่าง

คำตอบ:


90

สคริปต์นี้ใช้ตัวนับnเพื่อจำกัดความพยายามที่คำสั่งถึงห้า หากคำสั่งประสบความสำเร็จ$?จะถือเป็นศูนย์และการดำเนินการจะหยุดลง

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

1
คุณควรเพิ่มbreakถ้าคำสั่งสำเร็จแล้วมันจะหยุดลูป
Rahul Patil

อันที่จริงวิธีที่เหมาะสมในการเขียนที่if command; then break; fiสั้นหรือกระชับกว่านั้นคือcommand && break
tripleee

1
"command" เป็นเพียงชื่อของคำสั่งที่คุณต้องการตรวจสอบสถานะของ
สงสัย

3
น่าสังเกตว่าคุณสามารถทดสอบว่า n เท่ากับห้าในตอนท้ายเพื่อทราบว่าคำสั่งสำเร็จหรือไม่
mattdm

4
วิธีแก้ปัญหาที่ดี - แต่ในกรณีที่เกิดข้อnผิดพลาดมันไม่จำเป็นต้องนอนเพิ่มอีกหนึ่งครั้งก่อนที่จะออก
Ron Rothman

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

แทนที่ "คำสั่ง" ด้วยคำสั่งของคุณ นี่คือการสันนิษฐานว่า "รหัสสถานะ = FAIL" หมายถึงรหัสส่งคืนที่ไม่เป็นศูนย์


รูปแบบ:

การใช้{..}ไวยากรณ์ ใช้งานได้ในเชลล์ส่วนใหญ่ แต่ไม่ใช่ BusyBox sh:

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

การใช้seqและการส่งรหัสทางออกของคำสั่งที่ล้มเหลว:

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

เหมือนด้านบน แต่ข้ามsleep 15หลังจากความล้มเหลวครั้งสุดท้าย เนื่องจากเป็นการดีกว่าที่จะกำหนดจำนวนลูปสูงสุดเพียงครั้งเดียวสิ่งนี้สามารถทำได้โดยการนอนที่จุดเริ่มต้นของลูปถ้าi > 1:

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

25
+1 - รวบรัดและชัดเจน ข้อเสนอแนะข้อหนึ่ง: ฉันจะแทนที่for i in 1 2 3 4 5ด้วยfor i in {1..5}เพราะง่ายต่อการบำรุงรักษา
Paddy Landau

5
เพียงแค่ทราบการทำงานนี้เพราะ&&มีการประเมินก่อน||เพราะความสำคัญ
gene_wood

6
หมายเหตุอื่นนี่จะส่งคืนรหัส 0 แม้ว่าจะcommandล้มเหลว
Henrique Zambon

3
@HenriqueZambon เพิ่มรุ่นที่จัดการได้เช่นกัน
Alexander

2
การนอนหลับนี้ไม่ได้เกิดขึ้นหลังจากความล้มเหลวครั้งสุดท้ายใช่ไหม ดูเหมือนว่ารอ 15 วินาทีโดยไม่จำเป็น ฉันคิดว่าคุณสามารถเช็ค[[ i -eq 5]]เป็นเงื่อนไขหรือก่อนนอนเพื่อหลีกเลี่ยงปัญหานี้
เดฟ 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
}

ตัวอย่าง:

retry ping invalidserver

สร้างผลลัพธ์นี้:

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

สำหรับโลกแห่งความเป็นจริงตัวอย่างการทำงานกับคำสั่งที่ซับซ้อนดูสคริปต์นี้


3
นี่เป็นทางออกที่ดี ฉันชอบที่มันออกจากสถานะไม่เป็นศูนย์หลังจากที่บางสิ่งบางอย่างล้มเหลวหลายครั้งเช่นกัน
Ben Liyanage

11

นี่คือฟังก์ชันสำหรับลองใหม่

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

ผลผลิต:

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

ฉันคัดลอกรหัสของคุณวางในไฟล์ใหม่ที่ชื่อว่า retry.sh และเพิ่มบรรทัด #! / bin / bash ที่ด้านบน ในขณะที่ทำงานกับคำสั่งของคุณในคำอธิบายฉันไม่เห็นอะไรเลยที่จะปรากฏขึ้นอีกครั้ง
java_enthu

คุณได้ลองbash retry.sh 3 ping -c1 localhost
Rahul Patil

ใช่ราหุลฉันลองแล้ว
java_enthu

ขออภัยฉันเป็น bizy .. ฉันได้ทำการทดสอบอีกครั้งมันใช้งานได้ตรวจสอบ output paste.ubuntu.com/6002711
Rahul Patil

นี่คือคำตอบที่หรูหราที่สุดในที่นี่จนถึงตอนนี้ถ้าคุณกำลังทำอะไรที่ไม่สำคัญ ขอบคุณที่สละเวลา
Jerry Andrews


5

นี่คือนามแฝง / สคริปต์หนึ่งบรรทัดที่ฉันโปรดปราน

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

จากนั้นคุณสามารถทำสิ่งต่าง ๆ เช่น:

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

และมันจะยังคงรันคำสั่งก่อนหน้าจนกว่าจะพบ "กระบวนการถัดไป"


1
ใน zsh ใช้แทนfc -e "#" fc -s
Ricardo Stuven

2

ฉันใช้สคริปต์นี้ที่ทำให้ลองใหม่ของคำสั่งที่กำหนดประโยชน์ของสคริปต์นี้คือถ้าล้มเหลวในการลองใหม่ทั้งหมดมันจะเก็บรหัสทางออก

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

อาจจะง่ายขึ้น


ฉันชอบความยืดหยุ่นของรุ่นนี้และวิธีการ verbose และอ่านรหัสเป็น!
yo.ian.g

เพื่อให้ตรงกับเสียงสะท้อนที่ล้มเหลวคุณสามารถเพิ่มเสียงสะท้อนที่ประสบความสำเร็จด้วย [$ ret_value -eq 0] หรือทดสอบ $ ret_value หลังจากนั้น
yo.ian.g

รุ่นนี้มีข้อดีของการไม่นอนหลังจากคำสั่งล้มเหลวเป็นครั้งสุดท้าย
อเล็กซานเด

1

ดูตัวอย่างด้านล่าง:

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

done

ฉันกำลังพยายามเชื่อมต่อพอร์ต 3389 บน localhost มันจะลองอีกครั้งจนกว่าจะล้มเหลว 5 ครั้งถ้าสำเร็จก็จะทำให้เกิดการวนซ้ำ

$? มันมีอยู่สถานะของคำสั่งถ้ามันหมายถึงหมายถึงคำสั่งประสบความสำเร็จในการทำงานถ้าอื่น ๆ กว่าศูนย์หมายถึงคำสั่ง fai

ดูเหมือนซับซ้อนเล็กน้อยอาจเป็นคนที่ทำได้ดีกว่านี้


ขอบคุณราหุล .. จะลองใช้สคริปต์อีกครั้งไหม
Sandeep Singh

โปรดตรวจสอบตอนนี้ฉันได้อัปเดตแล้ว
Rahul Patil

$?มันมีอยู่สถานะของคำสั่งถ้ามันหมายถึงหมายถึงคำสั่งประสบความสำเร็จในการทำงานถ้าอื่น ๆ กว่าศูนย์หมายถึงคำสั่งล้มเหลว
Rahul Patil

จำเป็นต้องระบุที่อยู่โฮสต์และพอร์ต เราสามารถทำได้โดยให้ตำแหน่งสคริปต์เท่านั้น
Sandeep Singh

แทนที่ด้วยคำสั่งใด ๆ ที่ให้รหัสสถานะการออกเป็น $?
ราหุลปาติล

1

คุณสามารถใช้loopคำสั่งได้ที่นี่เช่น:

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

ซึ่งจะทำสิ่งที่คุณทำทุก ๆ 15 วินาทีจนกว่ามันจะประสบความสำเร็จสูงสุดห้าครั้ง


0

นี่คือretryฟังก์ชั่นวนซ้ำสำหรับนักเขียนโปรแกรมที่ใช้งานได้:

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
}

ส่งคำสั่ง (หรือชื่อฟังก์ชัน) และจำนวนครั้งในการลองใหม่และระยะเวลาการพักระหว่างการลองใหม่ดังนี้:

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

สิ่งนี้ใช้ไม่ได้กับคำสั่งที่มีความยาวมากกว่าหนึ่งคำ: cmd = "echo blah blah" ... บรรทัด 10: [: blah: คาดหวังว่าจะได้เป็นจำนวนเต็ม ... ไม่ทำงานสำหรับท่อ ฯลฯ
Mercury00
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.