Tengo un script de shell que lee de la entrada estándar . En circunstancias excepcionales, no habrá nadie listo para proporcionar información, y el script debe agotar el tiempo de espera . En caso de tiempo de espera, el script debe ejecutar algún código de limpieza. ¿Cuál es la mejor manera de hacer eso?
Este script debe ser muy portátil , incluidos los sistemas Unix del siglo XX sin un compilador de C y los dispositivos integrados que ejecutan busybox, por lo que no se puede confiar en Perl, bash, cualquier lenguaje compilado e incluso POSIX.2 completo. En particular, $PPID
, read -t
trampas y perfectamente compatibles con POSIX no están disponibles. Escribir en un archivo temporal también está excluido; el script podría ejecutarse incluso si todos los sistemas de archivos están montados de solo lectura.
Solo para hacer las cosas más difíciles, también quiero que el script sea razonablemente rápido cuando no se agote el tiempo. En particular, también uso el script en Windows (principalmente en Cygwin), donde fork y exec son particularmente bajos, por lo que quiero mantener su uso al mínimo.
En pocas palabras, tengo
trap cleanup 1 2 3 15
foo=`cat`
y quiero agregar un tiempo de espera No puedo reemplazar cat
con el read
incorporado. En caso de tiempo de espera, quiero ejecutar la cleanup
función.
Antecedentes: este script adivina la codificación del terminal imprimiendo algunos caracteres de 8 bits y comparando la posición del cursor antes y después. El comienzo de la secuencia de comandos prueba que stdout está conectado a un terminal compatible, pero a veces el entorno está mintiendo (por ejemplo, se plink
establece TERM=xterm
incluso si se llama conTERM=dumb
). La parte relevante del script se ve así:
text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code
stty eol 0 eof n -echo # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position
initial_report=`tr -dc \;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc \;0123456789`
cleanup
# Compute and return initial_x - final_x
¿Cómo puedo modificar el script para que si tr
no ha leído ninguna entrada después de 2 segundos, se elimina y el script ejecuta la cleanup
función?
Respuestas:
¿Qué hay de esto?
Es decir: ejecute el comando de producción de salida y
sleep
en el mismo grupo de procesos, un grupo de procesos solo para ellos. Cualquier comando que regrese primero mata a todo el grupo de procesos.¿Alguien se preguntaría: Sí, la tubería no se utiliza; se omite usando las redirecciones. El único propósito es que el shell ejecute los dos procesos en el mismo grupo de procesos.
Como Gilles señaló en su comentario, esto no funcionará en un script de shell porque el proceso de script se eliminaría junto con los dos subprocesos.
Una forma de forzar que un comando se ejecute en un grupo de proceso separado es iniciar un nuevo shell interactivo:
Pero puede haber advertencias con esto (por ejemplo, ¿cuándo stdin no es un tty?). La redirección stderr está ahí para deshacerse del mensaje "Terminado" cuando se mata el shell interactivo.
Probado con
zsh
,bash
ydash
. ¿Pero qué hay de los viejos?B98 sugiere el siguiente cambio, trabajando en Mac OS X, con GNU bash 3.2.57, o Linux con guión:
-
1. Aparte de lo
setsid
que parece no ser estándar.fuente
kill 0
termina matando al llamador del script también. ¿Hay alguna forma portátil de forzar la tubería a su propio grupo de procesos?setprgp()
sinsetsid
por ahora :-(Ya está atrapando 15 (la versión numérica de
SIGTERM
, que es lo quekill
envía a menos que se le indique lo contrario), por lo que ya debería estar listo. Dicho esto, si está buscando pre-POSIX, tenga en cuenta que las funciones de shell tampoco pueden existir (provienen del shell de System V).fuente
cat
salir. Ya he experimentado un poco, pero no he encontrado nada con lo que esté satisfecho hasta ahora.$!
, creo que algunas de esas máquinas que uso rara vez no tienen control de trabajo, ¿están$!
disponibles universalmente?bash
cosas realmente horribles y específicas que hice una vez que involucraron abuso-o monitor
. Estaba pensando en términos de conchas verdaderamente antiguas cuando escribí eso (funcionó en v7). Dicho esto, creo que puede hacer cualquiera de las otras dos cosas: (1) fondo "lo que sea" ywait $!
, o (2) también enviarSIGCLD
/SIGCHLD
... pero en máquinas lo suficientemente viejas, esta última es inexistente o no portátil (la primera es Sistema III / V, este último BSD y V7 no tenían ninguno).$!
regresa al menos a V7, y ciertamente es anterior a algosh
similar a lo que sabía algo sobre el control del trabajo (de hecho, durante mucho tiempo/bin/sh
en BSD no hizo el control del trabajo; había que corrercsh
para obtenerlo, pero$!
estaba allí )$!
.Aunque coretuils a partir de la versión 7.0 incluye un comando de tiempo de espera, ha mencionado algunos entornos que no lo tendrán. Afortunadamente, pixelbeat.org tiene un script de tiempo de espera escrito
sh
.Lo he usado antes en varias ocasiones y funciona muy bien.
http://www.pixelbeat.org/scripts/timeout ( Nota: la secuencia de comandos a continuación se ha modificado ligeramente de la de pixelbeat.org, consulte los comentarios debajo de esta respuesta).
fuente
</dev/stdin
es un no-op.</dev/tty
le permitiría leer desde el terminal, lo cual es lo suficientemente bueno para mi caso de uso.¿Qué pasa con (ab) usar NC para esto
Me gusta;
O enrollado en una sola línea de comando;
La última versión, aunque parece extraña realmente funciona cuando la pruebo en un sistema Linux: mi mejor suposición es que debería funcionar en cualquier sistema, y si no, una variación de la redirección de salida puede resolver la portabilidad. El beneficio aquí es que no hay procesos en segundo plano involucrados.
fuente
nc
en algunos viejos Unix boxen, ni en muchos Linux embebidos.Otra forma de ejecutar la canalización en su propio grupo de procesos es ejecutar
sh -c '....'
en un pseudo-terminal utilizando elscript
comando (que aplica implícitamente lasetsid
función).fuente
script
en algunos viejos Unix boxen, ni en muchos Linux embebidos.La respuesta en https://unix.stackexchange.com/a/18711 es muy buena.
Quería lograr un resultado similar, pero sin tener que invocar explícitamente el shell nuevamente porque quería invocar las funciones existentes del shell.
Usando bash, es posible hacer lo siguiente:
Supongamos que ya tengo una función de shell
f
:Ejecutando esto veo:
fuente
fuente