Código de salida en tuberías ============================ Cuando la *shell* ejecuta una tubería: .. code-block:: bash orden1 | orden2 el código de salida (el que podemos consultar con *$?*) es siempre el código de la orden derecha:: $ true | false $ echo $? 1 $ false | true $ echo $? 0 Que no podamos acceder al código de salida de la primera orden puede suponernos, en ocasiones, un problema. Por ejemplo, supongamos que creamos dos funciones: una se encarga de procesar datos y generar los resultados (o sea, se encarga de la lógica del problema), mientras que la otra, simplemente, coge los resultados y los formatea adecuadamente. Por ejemplo, supongamos que creamos una función a la que le damos un rango de números y nos devuelve los caracteres imprimibles correspondientes, suponiendo que los números son códigos ASCII: .. code-block:: bash # Obtiene los caracteres imprimibles correspondientes # a un rango de códigos ASCII get_chars() { local ini="$1" fin="${2:-255}" # Posibles errores. [ $ini -le $fin ] || return 1 # Rango invertido. [ $ini -ge 32 -a $fin -le 255 ] || return 1 # Valores fuera de rango. for i in $(seq $ini $fin); do /usr/bin/printf "%d \x$(printf %x $i)\n" $i done } que al ejecutarse con argumentos **60** y **65** devuelve esto:: 60 < 61 = 62 > 63 ? 64 @ 65 A La función puede fallar, si incluimos múmeros fuera del rango 32-255 que son los caracteres imprimibles. Esta función, no obstante, no se encarga de crear una salida bonita, sólo de resolver el problema en sí. Podríamos hacer que la propia función formateara, pero supongamos que preferimos hacerlo en una función aparte para separar la lógica de la presentación. En consecuencia creamos esta función: .. code-block:: bash formatea() { while read -r code char; do printf "%.25s %s\n" "$code......................................." "$char" done } que obrará el cambio: al ejecutarse así:: get_chars 60 65 | formatea de modo que generará una salida más agradable:: 60....................... < 61....................... = 62....................... > 63....................... ? 64....................... @ 65....................... A El problema de esta solución es que si en el programa en que incluimos estas funciones, queremos saber si la operación falló, nos será imposible puesto que la segunda función siempre devolverá éxito, incluso cuando no tenga nada que formatear. :program:`bash` soluciona de forma muy sencilla esto de dos formas: #. Consultando el array *PIPESTATUS* que almacena los estados de todos los programas que intervienen en la tubería. En el ejemplo *${PIPESTATUS[0]}*, almacena el código de salida de *get_chars* y *${PIPESTATUS[1]}* el de *formatea*. #. Usando la opción *pipefail*:: set -o pipefail que provoca que se devuelva el código de salida del programa más a la derecha que falló\ [#]_ y sólo se devuelva **0**, si no falló ningún programa. Pero estas facilidades no existen en el estándar *POSIX*. Por ello, hay que buscarle las vueltas al problema. Una solución no demasiado compleja es la siguiente\ [#]_: .. code-block:: bash { { { get_chars 60 65 3>&- 4>&-; echo $? >&3; } | formatea >&4; } 3>&1 | { read XC; exit $XC; } } 4>&1 que consiste en jugar con las redirecciones para lograr que el código de salida de *get_chars* sea leído por la orden :ref:`read `. Como este :command:`read` está en el último miembro de la tubería, y salimos de él con el código leído, logramos que el código de salida resultante sea el código de salida del miembro izquierdo. Como es un poco engorroso contruir la expresión y, además, se ofusca mucho la tubería, podemos crear una función que equivalga a lo anterior: .. todo:: Generalizar la solución .. code-block:: bash # # En una tubería simple de dos meimbros devuelve # el código de salida de la orden izquierda. # pf() { local XC run while [ $# -gt 0 ]; do if [ "$1" = "|" ]; then run="$run "'3>&- 4>&-; echo $? >&3; } ' break else run="$run '$1'" fi shift done eval "{ { { " $run "$@" '>&4; } 3>&1 | { read XC; return $XC; } } 4>&1' } Con ella podemos ejecutar así la tubería: .. code-block:: bash pf get_chars 60 65 \| formatea .. note:: Observe que hay que proteger la tubería para que la *shell* no la interprete. .. rubric:: Notas al pie .. [#] Obsérvese que se pueden anidar varias tuberías:: orden1 | orden2 | orden3 de modo que no particularicemos para dos. .. [#] Véase `esta respuesta en unix.stackexchange.com `_.