Estado de salida de Bash utilizado con PIPE

Estado de salida de Bash utilizado con PIPE

Estoy tratando de entender cómo se comunica el estado de salida cuando se utiliza una tubería. Supongamos que estoy usando whichpara localizar un programa inexistente:

which lss
echo $?
1

Como whichno pude localizar, lssobtuve un estado de salida de 1. Esto está bien. Sin embargo, cuando intento lo siguiente:

which lss | echo $?
0

Esto indica que el último comando ejecutado salió normalmente. La única forma en que puedo entender esto es que quizás PIPE también produzca un estado de salida. ¿Es esta la forma correcta de entenderlo?

Respuesta1

El estado de salida de una tubería es el estado de salida del comando de la derecha. Se ignora el estado de salida del comando de la izquierda.

(Ten en cuenta que which lss | echo $?no muestra esto. Deberías ejecutar which lss | true; echo $?para mostrar esto. En which lss | echo $?,echo $? informa el estado del último comando antes de esta canalización).

La razón por la que los shells se comportan de esta manera es que existe un escenario bastante común en el que se debe ignorar un error en el lado izquierdo. Si el lado derecho sale (o más generalmente cierra su entrada estándar) mientras el lado izquierdo todavía está escribiendo, entonces el lado izquierdo recibe una señal SIGPIPE. En este caso, normalmente no hay nada malo: al lado derecho no le importan los datos; Si el papel del lado izquierdo es únicamente producir estos datos, entonces está bien que se detenga.

Sin embargo, si el lado izquierdo muere por alguna razón distinta a SIGPIPE, o si el trabajo del lado izquierdo no era únicamente producir datos en salida estándar, entonces un error en el lado izquierdo es un error genuino que debe ser reportado.

En sh simple, la única solución es usar una tubería con nombre.

set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1

En ksh, bash y zsh, puede indicarle al shell que realice una salida de la tubería con un estado distinto de cero si algún componente de la tubería sale con un estado distinto de cero. Necesitas configurar la pipefailopción:

  • ksh:set -o pipefail
  • intento:shopt -s pipefail
  • zsh: setopt pipefail(o setopt pipe_fail)

En mksh, bash y zsh, puede obtener el estado de cada componente de la tubería usando la variable PIPESTATUS(bash, mksh) o pipestatus(zsh), que es una matriz que contiene el estado de todos los comandos en la última tubería (una generalización de $?).

Respuesta2

El estado de salida de la canalización es el estado de salida del último comando de la canalización (a menos que la opción de shellpipefail esté configurada en los shells que la admiten, en cuyo caso el estado de salida será el del último comando de la canalización que sale con un estado distinto de cero).

No es posible generar el estado de salida de una tubería.desde dentrouna tubería, ya que no hay forma de saber cuál sería ese valor hasta que la tubería haya terminado de ejecutarse.

La tubería

which lss | echo $?

no tiene sentido ya que echono lee su entrada estándar (las canalizaciones se utilizan para pasar datos entre la salida de un comando y la entrada del siguiente). Elecho imprimiría el estado de salida de la tubería, pero el estado de salida del comando se ejecutaría inmediatamente.antesla tubería.

$ false
$ which hello | echo $?
1
which: hello: Command not found.

$ true
$ which hello | echo $?
0
which: hello: Command not found.

Este es un mejor ejemplo:

$ echo hello | read a
$ echo $?
0

$ echo nonexistent | { read filename && rm $filename; }
rm: nonexistent: No such file or directory
$ echo $?
1

Esto significa que una tubería también se puede utilizar con if:

if gzip -dc file.gz | grep -q 'something'; then
  echo 'something was found'
fi

Respuesta3

Sí, PIPE produce un estado de salida; Si desea el código de salida del comando antes de PIPE, puede usar$PIPESTATUS

De acuerdo aestas preguntas y respuestas de desbordamiento de pila, para imprimir el estado de salida de un comando cuando usas una tubería puedes hacer:

your_command | echo $PIPESTATUS

O configure pipefail con el comando set -o pipefail(para desarmarlo, haga set +o pipefail) y use $?en lugar de $PIPESTATUS.

your_command | echo $?

Estos comandos sólo funcionan después de ejecutarlos al menos una vez; eso significa que PIPESTATUSsolo funciona cuando lo ejecutas después del comando con la tubería, así:

command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"

Recibirás 10 por ${PIPESTATUS[0]}y 1 por ${PIPESTATUS[1]}. Verestas preguntas y respuestas de U&Lpara más información.

Respuesta4

El shell evalúa la línea de comando antes de ejecutar la canalización. También lo $?es el valor del último comando ejecutado antes de la canalización. Si echose inicia antes o después whiches irrelevante.

Ahora, si su pregunta fue específicamente sobre la propagación del estado de salida, puede estudiar el comportamiento predeterminado de esta manera:

% false | true
% echo $?
0

% true | false
% echo $?
1

Como puede ver, el estado de salida de una canalización es el estado de salida de su último comando.

información relacionada