Як записати логіку повторного сценарію в сценарій, щоб продовжувати повторний запуск 5 разів?


112

Я хочу написати логіку в скрипті оболонки, який повторно повторить її запуск через 15 секунд до 5 разів на основі "коду статусу = 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
"команда" - це лише назва команди, для якої потрібно перевірити стан.
підозрюваний

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}тому що це легше підтримувати.
Падді Ландау

5
Лише зауважте, це працює, тому що &&оцінюється до цього ||через пріоритет оператора
gene_wood

6
Ще одна примітка: це поверне код 0, навіть якщо commandне вдасться.
Анріке Замбон

3
@HenriqueZambon Додана версія, яка також обробляє це.
Олександр

2
Хіба це не спить після остаточної невдачі? Здається, непотрібне чекання 15-х років. Я думаю, ви можете поставити чек на [[ i -eq 5]]стан АБО перед сном, щоб уникнути цього.
Дейв Лугг

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
Це чудове рішення. Мені подобається, що воно закінчується з ненульовим статусом виходу після того, як щось теж багато разів виходило з ладу.
Бен Ліянаж

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
Рахул Патіль

Так, Рахул, я намагався.
java_enthu

Вибачте, я був задумливим .., я знову перевірив, він працює, перевірити вихід paste.ubuntu.com/6002711
Rahul Patil

це найелегантніша відповідь поки що - якщо ви робите щось нетривіальне. Дякуємо, що знайшли час.
Джеррі Ендрюс


5

Ось мій улюблений псевдонім / сценарій

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

Тоді ви можете робити такі речі, як:

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

і він продовжуватиме виконувати попередню команду, поки не знайде "Наступний процес"


1
У zsh використовуйте fc -e "#"замість fc -s.
Рікардо Стювен

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

Напевно, це може стати простіше


Мені подобається, наскільки гнучка ця версія і наскільки багатослівний і читабельний код!
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

Здається, трохи складно, можливо, хтось зробить це краще, ніж це.


Дякую rahul .. буде тримати повтор для запуску сценарію ??
Sandeep Singh

Будь ласка, перевірте, я оновив
Rahul Patil

$?це існує статус команди, якщо вона нульова - команда успішно виконується, якщо крім нуля означає, що команда не працює
Рахул Патіль

чи потрібно вказати адресу хоста та порту. ми можемо це зробити, надавши лише dir-адресу для сценарію.
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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.