¿Ejecutar repetidamente un comando de shell hasta que falle?

191

He escrito una prueba difusa que falla de manera poco confiable. He agregado un código de depuración, pero ahora quiero ejecutar la prueba hasta que falle para poder reunir la salida de depuración.

He configurado la prueba para poder ejecutarla usando:

./runtest

Mi solución actual es escribir un untilfailscript:

#!/bin/bash
$@
while [ $? -eq 0 ]; do
    $@
done

Entonces úsalo:

untilfail ./runtest

¿Hay una solución más simple?

bradley.ayers
fuente
11
Nota al margen: cita habitualmente "$ @".
jordanm

Respuestas:

328

while toma un comando para ejecutar, por lo que puede usar el más simple

while ./runtest; do :; done

Esto detendrá el ciclo cuando ./runtestdevuelva un código de salida distinto de cero (que generalmente es indicativo de falla).

Sin embargo, para simplificar aún más su solución actual, solo debe cambiar su script de failfail para que se vea así:

#!/bin/bash

while "$@"; do :; done

Y luego puede llamarlo con cualquier comando que ya esté usando:

untilfail ./runTest --and val1,val2 -o option1 "argument two"
nneonneo
fuente
25
Es bueno señalar también que [es un comando. Es un error común con los nuevos usuarios que [es parte de ify whilesintaxis.
jordanm
2
¿Cómo podría obtener un recuento de cuántas veces se ejecutó antes de fallar?
GrantJ
13
@GrantJ: en realidad es muy simple. Poner count=0antes del bucle, luego, en lugar de :en el bucle (un no-op), poner (( count++ )): esto incrementa el contador.
nneonneo
14
Un truco de productividad: si tiene un sistema sayy un altavoz que puede usar while ./runtest; do :; done && say test failedpara recibir una notificación si alguna vez se detiene
Schneems
55
@Schneems: vale la pena señalar que sayes específico de macOS.
nneonneo
13

Si no desea ajustar una tubería compleja en un script o función de shell, esto funciona:

while true; do 
  curl -s "https:..." | grep "HasErrors.:true"
  if [[ "$?" -ne 0 ]]; then 
    break
  fi
  sleep 120
done

La solicitud HTTP en este caso siempre devuelve 200 pero también devuelve algunos JSON que tienen un atributo "HasErrors": verdadero cuando hay un error.

Judd Rogers
fuente
1

Habiendo tenido un problema similar en un sistema que tenía la lógica de reintento de shell duplicada en todas partes, hice una herramienta dedicada para resolver esto llamada "reintento":

retry --until=fail ./runtest

Un ejemplo más complejo:

retry --until=fail --message="test succeeded" --delay=1 ./runtest

Herramienta disponible en https://github.com/minfrin/retry .

Graham Leggett
fuente