Descripción general
En este tutorial, veremos las diversas técnicas para depurar scripts de shell Bash. El shell de Bash no proporciona ningún depurador integrado. Sin embargo, hay ciertos comandos y construcciones que se pueden utilizar para este propósito.
Primero, discutiremos los usos del comando set para depurar scripts. Después de eso, comprobaremos algunos casos de uso específicos de depuración utilizando los comandos set y trap. Finalmente, presentaremos algunos métodos para depurar scripts que ya están en ejecución.
Opciones de depuración de Bash
Las opciones de depuración disponibles en el shell de Bash se pueden activar y desactivar de varias maneras. Dentro de los scripts, podemos usar el comando set o agregar una opción a la línea shebang. Sin embargo, otro enfoque es especificar explícitamente las opciones de depuración en la línea de comandos mientras se ejecuta el script. Vamos a sumergirnos en la discusión.
2.1. Habilitar el Modo detallado
Podemos habilitar el modo detallado usando el conmutador-v, que nos permite ver cada comando antes de que se ejecute.
Para demostrar esto, vamos a crear un script de ejemplo:
#! /bin/bashread -p "Enter the input: " valzero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi
Este script comprueba si el número introducido como entrada es positivo o no.
A continuación, ejecutemos nuestro script:
$ bash -v ./positive_check.sh#! /bin/bashread -p "Enter the input: " valEnter the input: -10zero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fiThe input value is not positive.
Como podemos notar, imprime cada línea del script en el terminal antes de procesarlo.
También podemos añadir la opción-v en la línea shebang:
#! /bin/bash -v
Esto tiene el mismo efecto que llamar explícitamente a un script usando bash-v. Otro equivalente es habilitar el modo dentro de un script usando el comando set:
#! /bin/bashset -v
De hecho, podemos usar cualquiera de las formas mencionadas anteriormente para habilitar los diversos switches que discutiremos a partir de ahora.
2.2. Comprobación de sintaxis Utilizando el Modo noexec
Puede haber situaciones en las que deseemos validar sintácticamente el script antes de su ejecución. Si es así, podemos usar el modo noexec usando la opción-n. Como resultado, Bash leerá los comandos pero no los ejecutará.
Ejecutemos nuestro positive_check.sh script en modo noexec:
$ bash -n ./positive_check.sh
Esto produce una salida en blanco ya que no hay errores de sintaxis. Ahora, modificaremos un poco nuestro script y eliminaremos la instrucción then:
#! /bin/bashread -p "Enter the input: " valzero_val=0if echo "Positive number entered."else echo "The input value is not positive."fi
A continuación, lo validaremos sintácticamente con la opción-n:
$ bash -n ./positive_check_noexec.sh./positive_check_noexec.sh: line 6: syntax error near unexpected token `else'./positive_check_noexec.sh: line 6: ` else'
Como era de esperar, lanzó un error ya que perdimos la declaración then en la condición if.
2.3. Depuración Usando el modo xtrace
En la sección anterior, probamos el script en busca de errores de sintaxis. Pero para identificar errores lógicos, es posible que queramos rastrear el estado de las variables y los comandos durante el proceso de ejecución. En tales casos, podemos ejecutar el script en modo xtrace (seguimiento de ejecución) utilizando la opción-x.
Este modo imprime el trazo de comandos para cada línea después de que se expanden pero antes de que se ejecuten.
Ejecutemos nuestro positive_check.sh script en modo de seguimiento de ejecución:
$ bash -x ./positive_check.sh+ read -p 'Enter the input: ' valEnter the input: 17+ zero_val=0+ ''+ echo 'Positive number entered.'Positive number entered.
Aquí podemos ver la versión expandida de variables en stdout antes de la ejecución. Es importante tener en cuenta que las líneas precedidas por el signo + son generadas por el modo xtrace.
2.4. Identificación de variables no configuradas
Ejecutemos un experimento para comprender el comportamiento predeterminado de las variables no configuradas en scripts Bash:
#! /bin/bashfive_val=5two_val=2total=$((five_val+tow_val))echo $total
Ahora ejecutaremos el script anterior:
$ ./add_values.sh5
Como podemos observar, hay un problema: El script se ejecutó correctamente, pero la salida es lógicamente incorrecta.
Ahora ejecutaremos el script con la opción-u:
$ bash -u ./add_values.sh./add_values.sh: line 4: tow_val: unbound variable
Ciertamente, ¡ahora hay mucha más claridad!
El script no se pudo ejecutar ya que la variable tow_val no está definida. Habíamos escrito erróneamente two_val como tow_val mientras calculábamos el total.
La opción-u trata las variables y parámetros no configurados como un error al realizar la expansión de parámetros. En consecuencia, recibimos una notificación de error de que una variable no está vinculada al valor mientras se ejecuta el script con la opción-u
Casos de uso para Depurar Scripts de shell
Hasta ahora, vimos varios conmutadores para depurar scripts. A partir de ahora, veremos algunos casos de uso y métodos para implementarlos en scripts de shell.
3.1. Combinando opciones de depuración
Para obtener una mejor información, podemos combinar aún más las diversas opciones del comando set.
Ejecutemos nuestro add_values.sh script con las opciones-v y-u habilitadas:
$ bash -uv ./add_values.sh#! /bin/bashfive_val=5two_val=2total=$((five_val+tow_val))./add_values.sh: line 4: tow_val: unbound variable
Aquí, al habilitar el modo detallado con la opción-u, podríamos identificar fácilmente la instrucción que desencadena el error.
Del mismo modo, podemos combinar el modo detallado y xtrace para obtener información de depuración más precisa.
Como se discutió anteriormente, la opción-v muestra cada línea antes de ser evaluado, y la opción-x muestra cada línea después de que se amplió. Por lo tanto, podemos combinar las opciones-x y-v para ver cómo se ven las sentencias antes y después de las sustituciones de variables.
Ahora, ejecutemos nuestro positive_check.sh script con los modos-x y-v habilitados:
$ bash -xv ./positive_check.sh#! /bin/bashread -p "Enter the input: " val+ read -p 'Enter the input: ' valEnter the input: 5zero_val=0+ zero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi+ ''+ echo 'Positive number entered.'Positive number entered.
Podemos observar que las instrucciones se imprimen en la salida estándar antes y después de la expansión de la variable.
3.2. Depurar Partes específicas del Script
Depurar con scripts de shell de opción-x o-v genera una salida para cada instrucción en stdout. Sin embargo, puede haber situaciones en las que queramos reducir la información de depuración a solo partes específicas del script. Podemos lograrlo activando el modo de depuración antes de que se inicie el bloque de código, y luego restablecerlo usando el comando set.
Comprobémoslo con un ejemplo:
#! /bin/bashread -p "Enter the input: " valzero_val=0set -xif then echo "Positive number entered."else echo "The input value is not positive."fiset +xecho "Script Ended"
Aquí, podríamos depurar solo la condición if usando la instrucción set antes de que comience la condición. Más tarde, podríamos restablecer el modo xtrace después de que el bloque if termine usando el comando set +x.
Validémoslo con la salida:
$ ./positive_debug.shEnter the input: 7+ ''+ echo 'Positive number entered.'Positive number entered.+ set +xScript Ended
Ciertamente, la salida parece menos desordenada.
3.3. Redirigir solo la salida de depuración a un archivo
En la sección anterior, examinamos cómo podemos restringir la depuración solo a ciertas partes del script. En consecuencia, podríamos restringir la cantidad de salida en stdout.
Además, podemos redirigir la información de depuración a otro archivo y dejar que la salida del script se imprima en stdout.
Vamos a crear otro script para comprobarlo:
#! /bin/bashexec 5> debug.log PS4='$LINENO: ' BASH_XTRACEFD="5" read -p "Enter the input: " valzero_val=0if then echo "Positive number entered."else echo "The input value is not positive."fi
Primero, abrimos la depuración.archivo de registro en descriptor de archivo (FD) 5 para escribir con el comando exec.
Luego cambiamos la variable especial de shell PS4. La variable PS4 define el indicador que se muestra cuando ejecutamos un script de shell en modo xtrace. El valor predeterminado de PS4 es +. Cambiamos el valor de la variable PS4 para mostrar los números de línea en el indicador de depuración. Para lograr esto, utilizamos otra variable de shell especial LINENO.
Más tarde, asignamos la variable FD 5 a Bash BASH_XTRACEFD. En efecto, Bash ahora escribirá la salida xtrace en FD5, es decir, depurar.archivo de registro. Vamos a ejecutar el script:
$ bash -x ./debug_logging.sh+ exec+ PS4='$LINENO: '4: BASH_XTRACEFD=5Enter the input: 2Positive number entered.
Como era de esperar, la salida de depuración no se escribe en el terminal. Sin embargo, se imprimieron las primeras líneas, hasta que se asignó FD 5 a la salida de depuración.
Además, el script también crea una depuración de archivos de salida.registro, que contiene la información de depuración:
$ cat debug.log5: read -p 'Enter the input: ' val6: zero_val=07: ''9: echo 'Positive number entered.'
Scripts de depuración Usando trap
Podemos utilizar la función de trap de DEPURACIÓN de Bash para ejecutar un comando de forma repetitiva. El comando especificado en los argumentos del comando trap se ejecuta antes de cada instrucción posterior en el script.
Ilustremos esto con un ejemplo:
#! /bin/bashtrap 'echo "Line- ${LINENO}: five_val=${five_val}, two_val=${two_val}, total=${total}" ' DEBUGfive_val=5two_val=2total=$((five_val+two_val))echo "Total is: $total"total=0 && echo "Resetting Total"
En este ejemplo, especificamos el comando echo para imprimir los valores de las variables five_val, two_val y total. Posteriormente, pasamos esta instrucción echo al comando trap con la señal de DEPURACIÓN. En efecto, antes de la ejecución de cada comando en el script, se imprimen los valores de las variables.
Comprobemos la salida generada:
$ ./trap_debug.shLine- 3: five_val=, two_val=, total=Line- 4: five_val=5, two_val=, total=Line- 5: five_val=5, two_val=2, total=Line- 6: five_val=5, two_val=2, total=7Total is: 7Line- 7: five_val=5, two_val=2, total=7Line- 7: five_val=5, two_val=2, total=0Resetting Total
Depuración de scripts que ya se están ejecutando
Hasta ahora, presentamos métodos para depurar scripts de shell mientras se ejecutan. Ahora, veremos formas de depurar un script que ya se está ejecutando.
Considere un script en ejecución de ejemplo que ejecuta el sueño en un bucle while infinito:
#! /bin/bashwhile :do sleep 10 & echo "Sleeping for 4 seconds.." sleep 4done
Con la ayuda del comando pstree, podemos comprobar los procesos hijos bifurcados por nuestro script sleep.sh:
$ pstree -pinit(1)─┬─init(148)───bash(149)───sleep.sh(372)─┬─sleep(422) │ ├─sleep(424) │ └─sleep(425) ├─init(213)───bash(214)───pstree(426) └─{init}(7)
Utilizamos una opción adicional-p para imprimir los ID de proceso junto con los nombres de proceso. Por lo tanto, podemos darnos cuenta de que el script está esperando a que se completen los procesos secundarios (sueño).
A veces es posible que deseemos tener una mirada más cercana a las operaciones realizadas por nuestros procesos. En tales casos, podemos usar el comando strace para rastrear las llamadas al sistema Linux en curso:
$ sudo strace -c -fp 372strace: Process 372 attachedstrace: Process 789 attachedstrace: Process 790 attached^Cstrace: Process 372 detachedstrace: Process 789 detachedstrace: Process 790 detached% time seconds usecs/call calls errors syscall------ ----------- ----------- --------- --------- ----------------100.00 0.015625 5208 3 wait4 0.00 0.000000 0 6 read 0.00 0.000000 0 1 write 0.00 0.000000 0 39 close 0.00 0.000000 0 36 fstat 0.00 0.000000 0 38 mmap 0.00 0.000000 0 8 mprotect 0.00 0.000000 0 2 munmap 0.00 0.000000 0 6 brk 0.00 0.000000 0 16 rt_sigaction 0.00 0.000000 0 20 rt_sigprocmask 0.00 0.000000 0 1 rt_sigreturn 0.00 0.000000 0 6 6 access 0.00 0.000000 0 1 dup2 0.00 0.000000 0 2 getpid 0.00 0.000000 0 2 clone 0.00 0.000000 0 2 execve 0.00 0.000000 0 2 arch_prctl 0.00 0.000000 0 37 openat------ ----------- ----------- --------- --------- ----------------100.00 0.015625 228 6 total
Aquí usamos la opción-p para adjuntar al id de proceso (372), es decir, nuestro script en ejecución. Además, también utilizamos la opción-f para adjuntar a todos sus procesos hijos. Tenga en cuenta que el comando strace genera resultados para cada llamada al sistema. Por lo tanto, utilizamos la opción-c para imprimir un resumen de las llamadas al sistema al finalizar strace.
Conclusión
En este tutorial, estudiamos varias técnicas para depurar un script de shell.
Al principio, discutimos las diversas opciones de set command y su uso para depurar scripts. Después de eso, implementamos varios estudios de caso para estudiar una combinación de opciones de depuración. Además de esto, también exploramos formas de restringir la salida de depuración y redirigirla a otro archivo.
A continuación, presentamos un caso de uso del comando trap y la señal de depuración para escenarios de depuración. Finalmente, ofrecimos algunos enfoques para depurar scripts que ya se están ejecutando.