Tubería semi-asíncrona

11

Supongamos que tengo la siguiente tubería:

a | b | c | d

¿Cómo puedo esperar para completar c(o b) en sho bash? Esto significa que la secuencia de comandos dpuede iniciarse en cualquier momento (y no es necesario esperar), pero requiere que la salida completa de cfuncione correctamente.

El caso de uso es difftoolpara gitcomparar imágenes. Es invocado por gity necesita procesar su entrada (la a | b | cparte) y mostrar los resultados de la comparación (la dparte). La persona que llama eliminará la entrada que se requiere para ay b. Esto significa que antes de regresar del script, el proceso c(o b) debe finalizar. Por otro lado, no puedo esperar dporque esto significa que estoy esperando la entrada del usuario.

Sé que puedo escribir los resultados cen un archivo temporal, o tal vez usar un FIFO en bash. (Sin embargo, no estoy seguro si el FIFO ayudará). ¿Es posible lograr esto sin archivos temporales sh?

EDITAR

Quizás sería suficiente si pudiera encontrar la identificación del proceso del c(o b) proceso de manera confiable. Entonces, toda la tubería podría iniciarse de forma asincrónica, y podría esperar una ID de proceso. Algo en la línea de

wait $(a | b | { c & [print PID of c] ; } | d)

EDITAR ^ 2

He encontrado una solución, los comentarios (o soluciones aún mejores) son bienvenidos.

krlmlr
fuente
¿Quiere decir que desea dcomenzar a procesar cla salida solo después de que se chaya completado? ¿No desea dcomenzar a procesar cada línea de salida tal como viene?
terdon
@terdon: No, des libre de comenzar cuando lo desee, pero cdebe terminar antes de que pueda seguir adelante.
krlmlr
Eso parece ser contradictorio, si dpuede comenzar cuando le gusta, ¿qué está esperando exactamente?
terdon
@terdon: ampliado para mostrar el caso de uso.
krlmlr
Si dno utiliza la salida de c, parece que no tiene sentido formar dparte de la tubería. Pero si dusa la entrada, entonces ddebe trabajar un poco en su entrada después de haberla leído todo para que su enfoque marque la diferencia.
Hauke ​​Laging

Respuestas:

6
a | b | { c; [notify];} | d

La notificación se puede hacer, por ejemplo, mediante una señal a un PID que se ha pasado en una variable de entorno ( kill -USR1 $EXTPID) o creando un archivo ( touch /path/to/file).

Otra idea:

Ejecuta el siguiente proceso (el que puede comenzar a esperar) desde la tubería:

a | b | { c; exec >&-; nextprocess;} | d

o

a | b | { c; exec >&-; nextprocess &} | d
Hauke ​​Laging
fuente
Gracias. Pero luego, crear un archivo temporal para la salida intermedia es más detallado y más fácil de analizar (para un ser humano). Además, ¿cómo evito la condición de carrera con la señal? ... esperaba una solución más limpia, pero si este es el camino a seguir, que así sea.
krlmlr
@krlmlr ¿Qué condición de carrera?
Hauke ​​Laging
notifypodría ejecutarse antes de configurar la trampa de señal. ¿Cómo puedo asegurarme de no perder esa señal?
krlmlr
@krlmlr Detiene el script que llama a la canalización hasta que recibe una señal que se envía después de que trapse haya definido.
Hauke ​​Laging
Su edición: la tubería se llama en un bucle, sobre el cual no tengo control. Lamentablemente, { c; bg; }o { c; exit 0; }no parece funcionar.
krlmlr
5

Si entiendo su pregunta correctamente, esto debería funcionar:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(El truco de fd 3 se debe a que algunos (la mayoría) shells redirigen stdin a / dev / null with &).

Stéphane Chazelas
fuente
4

En bash, puedes usar una sustitución de proceso . El comando en la sustitución del proceso se ejecuta de forma asincrónica, no se espera.

a | b | c > >(d)
Gilles 'SO- deja de ser malvado'
fuente
Gracias. Esto es bashsolo, ¿no es shcompatible?
krlmlr
@krlmlr Bash / zsh solamente (y ksh93 con algunas modificaciones).
Gilles 'SO- deja de ser malvado'
3

Esto es lo que he encontrado por prueba y error, con la ayuda de la aportación de Hauke:

a | b | { c; kill -PIPE $$; } | d

Equivalentemente:

a | b | ( c; kill -PIPE $$; ) | d

(Este último es más explícito, porque de {}todos modos se ejecutará en una subshell si está dentro de una tubería).

Algunas otras señales (incluidas QUIT, TERMy USR1) también funcionan, sin embargo, en este caso, la descripción de la señal se muestra en el terminal.

Me pregunto si esta es la intención original de la PIPEseñal. De acuerdo con el manual :

SIGPIPE: La PIPEseñal se envía a un proceso cuando intenta escribir en una tubería sin un proceso conectado al otro extremo.

Esto significa que cuando envío artificialmente una señal de tubería a la subshell, finaliza en silencio, dejando solo al consumidor final ( d).

Esto funciona en ambos shy bash.

krlmlr
fuente
0

Puede usar el spongeprograma desde el paquete "moreutils":

a | b | c | sponge | d

la esponja será para el final de cla salida antes de conectarla d. Espero que eso sea lo que querías.

Minijackson
fuente
0

Solo puedes hacer:

a | b | c | (d ; cat > /dev/null)

Entonces, cuando dtermina, catabsorberá el resto de cla producción hasta que termine.

Okay. Después de los comentarios, creo que la respuesta es comenzar directamente den segundo plano.

Hacer:

a | b | c | (d &)

o use la solución de Stephane Chazelas si hay problemas con la dlectura de stdin .

angus
fuente
Me temo que mi caso de uso es más bien al revés: ctermina antes dy tengo que esperar hasta que ctermine, pero no me importa d.
krlmlr
Lo siento, no lo entiendo. ¿Qué quieres hacer cuando ctermine y dsiga funcionando? Matar d?
angus
dtiene una GUI y puede ejecutarse hasta que el usuario la cierre.
krlmlr
OK ... pero esto ya sucede. Cuando a, by cterminan su trabajo, cierran stdout y salen; y solo dsigue funcionando. ¿Quieres enviar dal fondo cuando ctermine? ¿Es asi?
angus
Sí, ddebe enviarse a un segundo plano una vez que cfinalice.
krlmlr