Para hacer que una tubería de zsh reporte un error ocurrido en cualquiera de sus fases, active la opción PIPE_FAIL antes de ejecutarla. En Terminal en Mac, introduzca setopt PIPE_FAIL; en un script, coloque esa línea antes de la tubería. De este modo, la tubería devolverá el estado de salida del comando fallido situado más a la derecha, o cero si todos los comandos se ejecutan con éxito. Capture $? inmediatamente después de la tubería si los comandos posteriores no deben reemplazar ese resultado. El manual de opciones de zsh documenta este comportamiento.

Por qué una tubería fallida puede parecer exitosa

De forma predeterminada, zsh asigna a una tubería el estado de salida de su comando final. Por lo tanto, si un comando anterior falla pero el comando final tiene éxito, la tubería completa puede aparentar haber tenido éxito. Por ejemplo, el cat final que se muestra a continuación puede tener éxito incluso cuando grep no encuentra ninguna línea coincidente:

printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?

El manual de zsh define una tubería como comandos unidos por | e indica que su valor normalmente es el valor del último comando. Un estado de cero significa éxito; un estado distinto de cero significa que el comando o la tubería reportó un error. La documentación de tuberías de zsh describe la regla predeterminada.

Habilitar PIPE_FAIL para la tubería

En una sesión de Terminal, establezca la opción antes de ejecutar el comando:

setopt PIPE_FAIL
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?

El estado final es ahora distinto de cero porque grep falló, a pesar de que cat tuvo éxito. PIPE_FAIL utiliza el estado distinto de cero situado más a la derecha en la tubería. Esto mantiene la significación del fallo mientras sigue devolviendo el código de estado real seleccionado por zsh. Para desactivar este comportamiento de nuevo en la misma sesión, ejecute unsetopt PIPE_FAIL.

Para un script, coloque la opción cerca del inicio, después de la línea del intérprete y antes de cualquier tubería afectada:

#!/bin/zsh
setopt PIPE_FAIL

printf '%s\n' 'ready' | grep 'missing' | cat
pipeline_status=$?

if (( pipeline_status != 0 )); then
  print -u2 -- "Pipeline failed with status $pipeline_status"
  exit "$pipeline_status"
fi

La asignación explícita del estado es importante: $? representa el estado del último comando, por lo que un print posterior, un comando relacionado con variables o un condicional pueden cambiar lo que reporta. Guardar el valor inmediatamente permite que el script lo reporte o salga con él después de realizar otras tareas. Este ejemplo devuelve un estado de fallo al programa que inició el script; no detiene la ejecución de los comandos individuales de la tubería tan pronto como falla una fase anterior.

Usar pipestatus cuando necesite el resultado de cada fase

PIPE_FAIL resume la tubería. Si también necesita identificar qué comando devolvió qué código, zsh proporciona el array especial pipestatus, que contiene el estado de cada comando en la última tubería. Léalo de inmediato, ya que otra tubería reemplazará esos valores. La referencia de parámetros de zsh documenta este array.

printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- "Stage statuses: ${pipestatus[*]}"

En este ejemplo de tres comandos, la salida muestra un cero para printf, un valor distinto de cero para grep y un cero para cat. El código exacto distinto de cero depende del comando. Si necesita tanto el resultado general como los resultados por fase, utilice PIPE_FAIL y capture el $? general de inmediato; inspeccione pipestatus inmediatamente después de una ejecución separada de la tubería al diagnosticar fases, o diseñe el script para registrar primero el array y calcular el agregado deseado a partir de esos datos guardados. Evite intercalar otro comando o tubería entre la tubería y el estado que desea leer.

Mantener la configuración en el shell adecuado

Para aplicar la opción a comandos posteriores en una sesión interactiva de zsh, ejecute setopt PIPE_FAIL en esa sesión. Para habilitarla en cada nueva sesión de zsh, agregue la línea a ~/.zshrc, y luego abra una nueva ventana de Terminal o cargue el archivo con source ~/.zshrc. La guía de Terminal de Apple identifica a zsh como el shell predeterminado, al tiempo que señala que los ajustes o los administradores pueden seleccionar un shell diferente. Confirme el shell activo antes de depender de la sintaxis específica de zsh. La guía de Terminal de Apple explica cómo Terminal elige el shell.

Un script que comienza con #!/bin/zsh solicita zsh directamente, independientemente del shell interactivo configurado para Terminal. Si, en cambio, el script se ejecuta mediante otro shell, setopt y pipestatus no son sustitutos portables de los propios controles de tuberías de ese shell. Mantenga alineados el intérprete y la sintaxis, y luego conserve el resultado de la tubería antes de realizar trabajos de seguimiento.

Fuentes