.. _sh-trap: .. index:: trap Captura de eventos ================== En ocasiones nos interesa ejecutar una acción o un conjunto de acciones al término del programa (p.e. borrar un archivo temporal creado durante la ejecución del *script*). Para ello existe la orden interna :command:`trap`:: trap [cadena_de_ordenes] SEÑAL1 [SEÑAL2 ...] La *cadena_de_ordenes* es una cadena que contiene la orden u órdenes que queremos ejecutar, mientras que a continuación se incluye la lista de señales para las que queremos que se ejecuten las órdenes. Por ejemplo:: TMPFILE=$(mktemp) trap 'rm -f $TMPFILE' EXIT TERM INT [... Continua el script ...] Esto hará que se borre el fichero temporal que hemos creado, cuando el script acaba (``EXIT``), se interrumple con *Ctrl+C* (``INT``) o se interrumpe de forma amable (con un :code:`kill -1`, p.e.). Un listado de señales posibles se puede obtener (en :command:`bash`) de:: $ trap -l La señal se puede incluir en :command:`trap` usando el número asociado o el nombre eliminando ``SIG-``. Además de estas señales, hay otros sucesos que pueden ser capturados: .. _bash-trap: ``EXIT`` (también el código **0**), que se produce cuando el programa acaba, bien porque no hay más código bien porque se topa con una orden :command:`exit`\ [#]_. ``DEBUG`` (sólo :command:`bash`), que se produce justamente antes de que una orden se ejecute. ``ERR`` (sólo :command:`bash`), que se produce cada vez que un comando falla. ``RETURN`` (sólo :command:`bash`), que se produce cada vez que una función termina su ejecución. En cuanto a la *cadena de órdenes* hay dos que tienen significado especial: #. La cadena vacía provoca que la señal se ignore. Por ejemplo:: #!/bin/sh echo 'Te vas a chupar diez segundos de espera, quieras o no' trap '' INT TERM sleep 10 .. note:: En realidad, esto no es un significado espacial. Literalmente, se hace lo que hemos dicho, nada, aunque hubiera alguna acción predefinida para esa señal. #. La ausencia de cadena o el guión ("-") hacen que la señal vuelva a su comportamiento habitual, o sea, que se anule un :command:`trap` que previamente hubieramos hecho:: #!/bin/sh echo 'Te vas a chupar diez segundos de espera, quieras o no' trap '' INT TERM sleep 10 echo 'Pero ahora sí te doy la oportunidad de que te canses de mí antes' trap - INT TERM sleep 10 .. warning:: El efecto no es acomulativo: si redefinimos la acción para una señal, la acción que hubiéramos asociado previamente, ya no se hará. Vea :ref:`más adelante cómo solvertar esto `. :command:`trap` también puede usarse sin argumento alguno, en cuyo caso mostrará las acciones definidas para cada señal:: $ trap 'echo "Hola"' INT $ trap trap -- 'echo "Hola"' SIGINT .. warning:: Aunque el `estándar POSIX dicta `_ que un :program:`trap` que se ejecute en una *subshell*, debe imprimir las acciones definidas en la *shell* padre:: $ echo $(trap) trap -- 'echo "Hola"' SIGINT $ trap | grep '^INT$' trap -- 'echo "Hola"' SIGINT :command:`dash` no lo hace y no devuelve nada\ [#]_ (sí cumplen el estándar :command:`bash`, como ilustra el ejemplo, y :command:`busybox`). .. _sh-trapplus: Por último, ya que :command:`trap` redefine, puede ser interesante disponer de una función que permita acomular acciones ante una señal o evento: .. literalinclude:: files/trapplus.sh :language: bash .. rubric:: Notas al pie .. [#] En los *scripts* propuestos para :ref:`tratar los argumentos ` hay un ejemplo de cómo mostrar siempre la ayuda cuando el programa acaba por un error en las opciones. .. [#] Así que si se quiere capturarse la salida, puede usarse esta argucia (la salida queda almacenada en la variable *PRE*): .. code-block:: bash { file($mktemp); trap ; PRE=$(cat); rm -f "$file"; } > "$file" < "$file"