
Estoy tratando de entender cómo se comunica el estado de salida cuando se utiliza una tubería. Supongamos que estoy usando which
para localizar un programa inexistente:
which lss
echo $?
1
Como which
no pude localizar, lss
obtuve 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 pipefail
opción:
- ksh:
set -o pipefail
- intento:
shopt -s pipefail
- zsh:
setopt pipefail
(osetopt 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 echo
no 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 PIPESTATUS
solo 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 echo
se inicia antes o después which
es 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.