¿Puede un programa siguiente en una tubería ver el código de salida del programa anterior?

8

Me gustaría hacer un pipeline de scripts de Bash como este

prog1 | prog2

tal que prog2 puede ver el código de salida de prog1 y actuar de manera diferente en función de esa información.

es posible?

dan
fuente
¿Podría elaborar el acto de manera diferente como parte de su pregunta?
devnull
Usted tiene un control limitado sobre cuánto prog2ha progresado cuando prog1sale, debido al almacenamiento en búfer interno utilizado para implementar la tubería y cómo prog1y cómo prog2están programados.
chepner
También mire [¿En qué orden se ejecutan los comandos canalizados?] (Unix.stackexchange.com/q/37508)
DK Bose

Respuestas:

4

La respuesta general es no. Es posible prog2salir antes prog1incluso de comenzar (obviamente, eso no puede suceder si prog2realmente lee alguna entrada, lo que esperaría que hiciera si lo está usando en una tubería). Definitivamente es posible prog2salir antes prog1; Esto sucede, por ejemplo, cuando se prog2trata de un programa de búsqueda que sale tan pronto como encuentra una coincidencia, en cuyo caso prog1puede que todavía no haya terminado de producir todos los datos.

No hay una forma directa de prog2recuperar el estado de salida prog1o incluso saber que prog1ha salido. Todo lo que prog2puede saber es que prog1ha cerrado su extremo de la tubería, lo que puede hacer sin morir.

Si desea obtener el estado de salida prog1de prog2, hay dos métodos comunes: se puede escribir en un archivo, o se puede enviar a través de la tubería. Enviar el estado de salida como la última línea de los datos canalizados es una posibilidad. Debe asegurarse de no procesar la última línea hasta que sepa que es la última línea, es decir, hasta que haya intentado leer la siguiente.

{ prog1; echo $?; } | 

Aquí hay un ejemplo donde el lado derecho es un filtro de texto que colorea cada línea que contiene la palabra "error" en rojo. Si el lado izquierdo falla, el lado derecho sale con el mismo estado.

{ prog1; echo $?; } | awk '
    NR != 1 {
        if (line ~ /[Ee][Rr][Rr][Oo][Rr]/) print "\033[31m" line "\033[0m";
        else print line;
    }
    {line = $0}
    END {exit($0)}
'
Gilles 'SO- deja de ser malvado'
fuente
Intenté { command; echo ${PIPESTATUS[@]}; } | sort | ...para que el estado de salida venga primero en la secuencia. ¡Todo es muy interesante!
@ illuminÉ Eso solo funcionaría si ${PIPESTATUS[@]}se ordena antes que cualquier otra cosa en la salida de command. Si commandimprime varios números, o si puede imprimir texto arbitrario, tiene problemas: no podrá distinguir su salida de la línea de estado.
Gilles 'SO- deja de ser malvado'
Gracias, de hecho, solo logra ordenar un estado de éxito al principio si el comando no contiene 0 en su salida jajaja. Tgif / s.
2

Aunque puede en algunos casos especiales (vea las otras respuestas) no puede en todos los casos. Algunos programas de filtro simplemente continuarán, mientras que otros mantendrán toda la salida, la desatarán de una sola vez y luego saldrán.

Para un ejemplo de un programa "simplemente continúe", grepservidor, como lo haría tail -f /var/log/some_log_file. El uso sorten una tubería provoca un "bloqueo", ya sortque recogerá la entrada hasta que la tubería frente a ella se cierre. El uso xargsagrega una complicación adicional: ¿son los programas iniciados por xargs(puede comenzar muchas instancias) parte de la tubería o no?

Bruce Ediger
fuente
-1 porque te votaron por racionalizar una respuesta incorrecta.
ctrl-alt-delor
@richard ¿Eh? La respuesta de Bruce es correcta (aunque el segundo párrafo es un poco confuso).
Gilles 'SO- deja de ser malvado'
1

La respuesta: no directamente.

@terdon ha ilustrado que el código de salida del comando anterior en la tubería debe enviarse como un parámetro explícito al siguiente comando.

Recuerde que la tubería es simplemente un mapeo del STDOUT del comando anterior al STDIN del siguiente comando; los códigos de salida no se envían a STDOUT (o STDERR).

pepoluan
fuente
1
-1 porque te votaron positivamente por citar una respuesta incorrecta y no decir mucho más.
ctrl-alt-delor
justo lo suficiente @richard ... Debería haber una doble comprobación ... eso es lo que pasó, si me esfuerzo para responder a una pregunta cuando muy cansado ...
pepoluan
1

Todos los procesos, en proceso, se inician antes de cualquier salida. Por prog2lo tanto, podría tener que obtener esta información después de que haya comenzado, también tendría que retrasar el procesamiento hasta que prog1haya salido, esto podría detener la tubería. Parece haber problemas fundamentales al hacer lo que pides, no limitaciones del sistema operativo.

Probablemente deba considerar un archivo temporal o colocar el resultado en una variable.

Ejemplo para una pequeña cantidad de datos, usando una variable.

tmp=$(prog1)
if test "z$PIPESTATUS" == "z0"
then
   
else
   
fi
ctrl-alt-delor
fuente
Hay un vacío en tu razonamiento. prog2se inicia antes de que se prog1complete en general, pero podría haber una forma de recibir el estado de salida prog1mientras se está ejecutando.
Gilles 'SO- deja de ser malvado'
0

Para terminar la respuesta de Gilles ,

(prog1; echo $? > /tmp/prog1.status) | prog2

Es un enfoque.  prog2podría

  • lea la entrada estándar hasta el final, y luego lea /tmp/prog1.status, o
  • compruebe la existencia de /tmp/prog1.statusperiódicamente mientras lee la entrada estándar.
Scott
fuente