.. _sh-debug: Depuración ========== :command:`sh`/:command:`bash` no es el mejor lenguaje del mundo a la hora de depurar. Es más, su tipado débil es una fuente permanente de errores triviales que a veces cuesta encontrar, como el de equivocarse en la digitalización del nombre de una variable. En cualquier caso, para facilitar la depuración (además de los supuestos orden, claridad y sencillez en la escritura del código), podemos usar las siguientes técnicas: :code:`set -u` Colocar esta sentencia al principio del *script* provoca que el intérprete genere un error cuando se usa una variable a la que no se ha asignado valor previamente. Por ejemplo:: $ num=1 $ echo $((num+1)) 2 $ echo $((nun+1)) # Pero si digitalizamos mal, bash se calla 1 $ set -u # Ahora sin embargo, no $ echo $((nun+1)) -bash: nun: variable sin asignar .. note:: Para que el intérprete revierta el efecto, puede escribirse lo mismo, pero con el signo "*+*" en vez de el "*-*":: $ set +u Tal cosa es aplicable a las demás técnicas que vemos a continuación. :code:`set -e` Provoca que el intérprete cancele la ejecución del programa nada más ejecutar una orden que devuelve error (o sea, un valor distinto de 0). :code:`set -v` Hace un eco en pantalla de todos las ordenes que se ejecutan. Esto puede servirnos en un programa para ver qué órdenes está realmente ejecutando el programa:: $ a=5 $ set -v $ echo $a echo $a 5 Es de prever que al depurar un error sepamos la zona por la que se encuentra el error. En ese caso, podríamos usar la sentencia justamente antes de que empiece el código problemático y revertir el efecto cuando ya sepamos que tal código ha pasado. :code:`set -x`: Muestra también la orden ejecutada, pero con las sustituciones ya hechas:: $ a=5 $ set -x $ echo $a + echo 5 5 .. note:: Pueden aplicarse :kbd:`-x` y :kbd:`-v` a la vez lo que propiciará que se nos muestre la orden original y la orden con sustituciones:: $ a=5 $ set -vx $ echo $a echo $a + echo 5 5 :command:`shellcheck` Es un analizador estático de código. Por ejemplo, para el código: .. code-block:: bash #!/bin/sh function foobar() { echo "Esta función no está declarada según el estándar" n=4 echo $((n + 1.5)) } foobar devolvería lo siguiente: .. code-block:: none $ shellcheck script.sh In /tmp/caca.sh line 5: function foobar() { ^-- SC2112: 'function' keyword is non-standard. Delete it. In /tmp/caca.sh line 8: echo $((n + 1.5)) ^-- SC2079: (( )) doesn't support decimals. Use bc or awk. **Ejecución paso a paso** Para emular la ejecución paso a paso de los depuradores típicos de otros lenguajes, debemos recurrir a :command:`bash` aprovechando que permite :ref:`usar el argumento DEBUG con trap `\ [#]_: .. literalinclude:: files/debugger.sh :language: bash Si incorporamos temporalemente estas tres funciones a nuestro código (lo más cómodo es dejarlas en un fichero aparte e cargarlas con :ref:`source `), no necesitamos más que añadir la sentencia :command:`breakpoint` allá donde queramos crear un punto de ruptura y que empiece la ejecucuon paso a paso y :command:`continue` a partir de allí donde queramos que la ejecución prosiga de forma normal. Al pararse la ejecución, podemos: * Presionar, simplemente, ":kbd:`Enter`" o escribir ":kbd:`n`" para ejecutar la línea y avanzar a la siguiente. * Escribir ":kbd:`c`" para que la ejecución continúe sin más paradas hasta el próximo *breakpoint*\ [#]_. * Evaluar cualquier órden, sin avanzar en absoluto. .. seealso:: En la fase de depuración (o incluso en la de producción para realizar comprobaciones) puede intersarnos definir :ref:`la función execute `. .. rubric:: Notas al pie .. [#] Aunque pretenemos hacer un *script* compatible con el estándar *POSIX*, vernos obligados a usar para esta tarea :command:`bash` no es un gran incoveniente, a menos claro está que nos encontremos en un sistema sin :command:`bash` .. [#] En realidad, dentro de la función *debug* no podemos modificar limpiar el evento *DEBUG*, por lo que pulsar "*c*" no evita que para las siguientes líneas regresemos a dicha función. Por ese motivo, usamos la argucia de usar una variable para ver si entramos en el *entorno* de depuración o no.