¿POSIX equivalente para tiempo de espera de GNU?

14

El timeoutcomando GNU coreutils es extremadamente útil para ciertas situaciones de secuencias de comandos, ya que permite utilizar la salida de un comando si se ejecuta rápidamente y omitirlo si lleva demasiado tiempo.

¿Cómo puedo aproximar el comportamiento básico de timeoutusar solo las utilidades POSIX especificadas?


(Estoy pensando que puede implicar una combinación de wait, sleep, killy quién sabe qué más, pero quizás me falta un enfoque más sencillo.)

Comodín
fuente
3
Consulte Tiempo de espera en un script de shell , pero no considero que esto sea un duplicado porque solicité la portabilidad a los sistemas pre-POSIX y tuve el requisito de preservar stdin y stdout, que algunas soluciones que involucran procesos en segundo plano descartan.
Gilles 'SO- deja de ser malvado'
command & pid=$! ; sleep 5 && kill $pid
Pandya
1
@Pandya, ¿eso no introduce una ligera condición de carrera, en la que si commandtermina rápidamente hay una pequeña posibilidad de pidque otro proceso se vuelva a utilizar antes de que se killejecute el comando? No querría eso en el código de producción ...
Wildcard
¿Puedes explicar más sobre el problema subyacente que estás tratando de resolver? ¿Por qué no solo compilar el timeoutprograma y usarlo?
James Youngman
2
@JamesYoungman, supongo que no estás muy familiarizado con la escritura de guiones para la portabilidad . Pedir una forma de hacer algo compatible con POSIX (o especificado por POSIX) implica que está destinado a ser portátil. Compilar código fuente en un binario no es enfáticamente portátil y, dependiendo de las políticas de seguridad de su empresa, es posible que no tenga un compilador instalado en absoluto en un servidor de producción.
Comodín el

Respuestas:

2

Mi enfoque sería este:

  • Ejecutar comando como proceso en segundo plano 1

  • Ejecute el "temporizador de vigilancia" como proceso en segundo plano 2

  • Configure un controlador para atrapar una señal de terminación en el shell principal

  • Espere a que se completen ambos procesos. El proceso que termina primero, envía la señal de terminación al padre.

  • El controlador de trampa de los padres mata ambos procesos en segundo plano a través del control de trabajo (uno de ellos ya ha terminado por definición, pero ese asesinato será una operación inofensiva porque no estamos usando PID, ver más abajo)

Traté de eludir la posible condición de carrera abordada en los comentarios mediante el uso de las ID de control de trabajo del shell (que sería inequívoco dentro de esta instancia del shell) para identificar los procesos en segundo plano para matar, en lugar de los PID del sistema.

#!/bin/sh

TIMEOUT=$1
COMMAND='sleep 5'

function cleanup {
    echo "SIGTERM trap"
    kill %1 %2
}

trap cleanup SIGTERM

($COMMAND; echo "Command completed"; kill $$) &
(sleep $TIMEOUT; echo "Timeout expired"; kill $$) &

wait
echo "End of execution"

Resultado para TIMEOUT=10(el comando termina antes del watchdog):

$ ./timeout.sh 10
Command completed
SIGTERM trap
End of execution

Resultado para TIMEOUT=1(watchdog termina antes del comando):

$ ./timeout.sh 1
Timeout expired
SIGTERM trap
End of execution

Resultado para TIMEOUT=5(watchdog y comando terminan "casi" simultáneamente):

./tst.sh 5
Timeout expired
Command completed
SIGTERM trap
End of execution
Guido
fuente
Esto no es compatible con POSIX ya que (a) la definición de la función debería ser cleanup() { ...; }y (b) el control del trabajo es una función bash no especificada por POSIX (aunque ksh y otros también lo tienen). Buen guión, sin embargo!
Comodín el
También puede mejorar con timeout="$1"; shifty en (exec "$@"; echo "Command completed"; kill $$)lugar de volver a dividir la lista de argumentos.
Comodín el