La depuración lo ayuda a corregir los errores en su programa. En este artículo, discutiremos varios métodos para depurar scripts bash en sistemas operativos Linux y Unix.
Introducción
En mis días iniciales de programación, pasé horas tratando de encontrar el error en mi código y, al final, podría ser algo simple. Es posible que también te hayas enfrentado a la misma situación.
Saber cómo usar la técnica de depuración adecuada lo ayudaría a resolver los errores rápidamente. A diferencia de otros lenguajes como Python, Java, etc., no existe una herramienta de depuración para bash en la que pueda establecer puntos de interrupción, pasar por alto el código, etc.
Hay algunas funciones integradas que ayudan a depurar los scripts de bash shell. Vamos a ver en detalle esas características en las próximas secciones.
Tres formas de usar las opciones de depuración
Cuando desee habilitar las opciones de depuración en sus scripts, puede hacerlo de tres maneras.
$ bash [ debugging flags ] scriptname
#!/bin/bash [ debugging flags ]
set
comando del script.
set -o nounset
set -u
¿Para qué se utiliza el comando Establecer?
El set
command es un comando integrado de shell que se puede usar para controlar los parámetros de bash y alterar el comportamiento de bash de ciertas maneras.
Normalmente, no ejecutará comandos de configuración desde la terminal para modificar el comportamiento de su shell. Será ampliamente utilizado dentro de los scripts de shell, ya sea para la depuración o para habilitar el modo estricto de bash.
$ type -a set
set is a shell builtin
Puede acceder a la sección de ayuda del comando set para saber qué banderas admite y qué hace cada bandera.
$ set --help
Depurar parte del script o script completo
Antes de conocer las opciones de depuración, debe comprender que puede depurar todo el script o solo una parte determinada del código. Debe usar el comando set para habilitar y deshabilitar las opciones de depuración.
set -<debugging-flag>
habilitará el modo de depuración.set +<debugging-flag>
deshabilitará el modo de depuración.
Echa un vistazo al siguiente código. set -x
habilitará el modo xtrace para el script y set +x
deshabilitará el modo xtrace. Cualquier cosa que se encuentre entre set -x
y set +x
se ejecutará en modo de depuración xtrace.
Aprenderá sobre el modo xtrace en la próxima sección. Entonces, para cualquier indicador de depuración, lo único que debe recordar es, set -
habilitará el modo y set +
desactivará el modo.
#!/bin/bash set -x read -p "Pass Dir name : " D_OBJECT read -p "Pass File name : " F_OBJECT set +x touch ${D_OBJECT}/${F_OBJECT}
Fallo si no se define ninguna variable
Cuando se trabaja con variables en bash , la desventaja es que si tratamos de usar una variable no definida, el script no fallará con algún mensaje de error como "Variable no definida" . En su lugar, imprimirá una cadena vacía.
Eche un vistazo al siguiente código donde obtengo información del usuario y la almaceno en la variable $OBJECT
. Intenté ejecutar el operador de prueba (-f
y -d
) en el $OBJECT1
variable que no está definida.
#!/bin/bash read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT1 ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT1 ]] then echo "$OBJECT is a directory" fi
Cuando ejecuto este código, debería haberme arrojado un error, pero no fue así e incluso el script salió con el código de retorno cero.
Para anular este comportamiento, use -u
indicador que arrojará un error cuando se use una variable indefinida.
Ejecutaré el mismo código nuevamente con el nombre de variable incorrecto, pero esta vez generará una "Variable no vinculada" error.
También puede establecer el -u
opción usando el set
ordene o páselo como argumento al shebang.
set -u
set -o nounset
(o)
#! /bin/bash -u
Modo Xtrace al rescate
Este es el modo que uso ampliamente cuando depuro scripts bash para errores lógicos. Xtrace El modo mostrará el código línea por línea pero con los parámetros expandidos.
En la sección anterior, cuando ejecuté el código sin -u
flag, se completó con éxito pero esperaba el resultado en la terminal. Ahora puedo ejecutar el mismo script en modo xtrace y ver exactamente dónde ocurre el problema en el script.
Eche un vistazo al siguiente código de ejemplo.
#!/bin/bash read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT1 ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT1 ]] then echo "$OBJECT is a directory" fi
Cuando ejecuto el código anterior, no me devuelve ningún resultado.
Para depurar este problema, puedo ejecutar el script en xtrace modo pasando el -x
bandera.
En el siguiente resultado, puede ver que las variables se expanden e imprimen. Esto me dice que hay cadenas vacías asignadas a las declaraciones condicionales -f
y -d
. De esta manera, lógicamente puedo verificar y corregir los errores.
El signo más que ve en la salida se puede cambiar configurando PS4
variable en el guión. De forma predeterminada, PS4 está configurado en (+
).
$ echo $PS4
+
$ PS4=" ==> " bash -x debugging.sh
También puede configurar el modo Xtrace usando el comando set o pasarlo como argumento al tinglado.
set -x
set -o xtrace
(o)
#! /bin/bash -x
De manera similar, al depurar puede redirigir los registros de depuración de Xtrace a un archivo en lugar de imprimirlos en la terminal.
Echa un vistazo al siguiente código. Estoy asignando un descriptor de archivo 6 a .log
archivo y BASH_XTRACEFD="6"
redirigirá los registros de depuración de xtrace al descriptor de archivo 6.
#!/bin/bash exec 6> redirected_debug.log PS4=' ==> ' BASH_XTRACEFD="6" read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT1 ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT1 ]] then echo "$OBJECT is a directory" fi
Cuando ejecuto este código en lugar de imprimir la salida de xtrace en la terminal, se redirigirá a .log
archivo.
$ cat redirected_debug.log ==> read -p 'Please provide the object name : ' OBJECT ==> [[ -f '' ]] ==> [[ -d '' ]]
Estado de salida de PIPE
El comportamiento predeterminado cuando se usa una tubería es que tomará el código de salida del último comando de ejecución en la tubería. Incluso si fallan los comandos anteriores en la tubería, ejecutará el resto de la tubería.
Eche un vistazo al siguiente ejemplo. Intenté abrir un archivo que no está disponible y canalizarlo con un recuento de palabras programa. Aunque el cat
comando arroja un error, se ejecuta el programa de conteo de palabras.
Si intenta verificar el código de salida del último comando de tubería de ejecución usando $?
, obtendrá cero como código de salida que es del programa de conteo de palabras.
$ cat nofile.txt | wc -l cat: nofile.txt: No such file or directory 0
$ echo $? 0
Cuando pipefail está habilitado en la secuencia de comandos, si algún comando arroja un código de retorno distinto de cero en la tubería, se considerará como el código de retorno para toda la tubería. Puede habilitar pipefail agregando la siguiente propiedad establecida en su secuencia de comandos.
set -o pipefail
Todavía hay un problema con este enfoque. Normalmente, lo que se debe esperar es que si algún comando en la canalización falla, el script debe salir sin ejecutar el resto del comando en la canalización.
Pero desafortunadamente, incluso si algún comando falla, el siguiente comando en la tubería se ejecuta. Esto se debe a que cada comando en la canalización se ejecuta en su propia subcapa. El shell esperará hasta que se completen todos los procesos en la canalización y luego devolverá el resultado.
Modo estricto de Bash
Para eliminar todos los posibles errores que hemos visto en secciones anteriores, se recomienda agregar las siguientes opciones en cada script.
Ya hemos discutido todas estas opciones en la sección anterior en detalle.
-e flag
=> Salga del script si algún comando arroja un código de salida distinto de cero.-u flag
=> Hacer que el script falle si se usa un nombre de variable indefinido.pipefail
=> Si algún comando en la tubería falla, el código de salida se considerará para toda la tubería.IFS
=> Separador de campo interno, establecerlo en nueva línea (\n) y (\t) hará que la división ocurra solo en nueva línea y tabulación.
set -e
set -u
set -o pipefail
O
set -euo pipefail
IFS=$'\n\t'
Captura señales usando TRAP
Trampa le permite capturar señales en su script bash y tomar algunas acciones en consecuencia.
Piense en un escenario en el que activa la secuencia de comandos pero desea cancelar la secuencia de comandos usando CTRL+C
pulsación de tecla En ese caso, SIGINT
será enviado a su script. Puede capturar esta señal y ejecutar algunos comandos o funciones.
Echa un vistazo al pseudocódigo que se muestra a continuación. Creé una función llamada limpieza que se ejecutará cuando SIGINT
se pasa al script.
trap 'cleanup' TERM INT function cleanup(){ echo "Running cleanup since user initiated CTRL + C" <some logic> }
Puede usar la trampa "DEBUG", que se puede usar para ejecutar una declaración repetidamente en el script. La forma en que se comporta es que cada declaración que se ejecuta en la captura de script ejecutará la función o declaración asociada.
Puedes entender esto usando el siguiente ejemplo.
#!/bin/bash trap 'printf "${LINENO} ==> DIR_NAME=${D_OBJECT} ; FILE_NAME=${F_OBJECT}; FILE_CREATED=${FILE_C} \n"' DEBUG read -p "Pass Dir name : " D_OBJECT read -p "Pass File name : " F_OBJECT touch ${D_OBJECT}/${F_OBJECT} && FILE_C="Yes" exit 0
Este es un programa simple que obtiene la entrada del usuario y crea un archivo y un directorio. El comando trap se ejecutará para cada declaración en el script e imprimirá los argumentos pasados y el estado de creación del archivo.
Echa un vistazo a la siguiente salida. Para cada línea del script, se activa la trampa y las variables se actualizan en consecuencia.
Imprime el código usando el modo Verbose
En el modo detallado, el código se imprimirá antes de devolver el resultado. Si el programa requiere una entrada interactiva, en ese caso solo se imprimirá esa línea seguida de un bloque de códigos.
Echa un vistazo al siguiente programa. Es un programa simple que obtiene un objeto del usuario y verifica si el objeto pasado es un archivo o directorio usando una declaración condicional .
#!/bin/bash read -p "Please provide the object name : " OBJECT if [[ -f $OBJECT ]] then echo "$OBJECT is a file" elif [[ -d $OBJECT ]] then echo "$OBJECT is a directory" fi
Cuando ejecuto el código anterior, primero imprimirá el código y luego esperará la entrada del usuario como se muestra a continuación.
Una vez que pasé el objeto, el resto del código se imprimirá seguido de la salida.
También puede configurar el modo detallado usando set
o en shebang
.
set -v
set -o verbose
(o)
#! /bin/bash -v
También puede combinar el modo detallado con otros modos.
set -vx # Verbose and Xtrace Mode
set -uv # Verbose and Unset Mode
Validación de sintaxis - modo noexec
Hasta ahora hemos visto cómo trabajar con errores lógicos en el script. En esta sección, analicemos los errores de sintaxis.
Los errores de sintaxis son muy comunes en los programas. Es posible que haya perdido una cita o no haya podido salir del ciclo, etc. Puede usar el "-n
" indicador que se llama noexec mode
para validar la sintaxis antes de ejecutar el programa.
Voy a ejecutar el siguiente código y validar la sintaxis.
#!/bin/bash TOOLS=( htop peek tilix vagrant shutter ) for TOOL in "${TOOLS[@]" do echo "--------------INSTALLING: ${TOOL}---------------------------" apt install ${TOOL} -y #done
Hay dos errores en este programa. En primer lugar, no pude cerrar las llaves en el "for loop
" y en segundo lugar done
la palabra clave está comentada, lo que debería marcar el final del ciclo.
Cuando ejecuto este programa, recibo los siguientes mensajes de error que indican que falta corchete y palabra clave lista . A veces, el número de línea que se señala en el mensaje de error no contendrá ningún error que deba buscar para encontrar el error real.
$ bash -n ./debugging.sh ./debugging.sh: line 6: unexpected EOF while looking for matching `"' ./debugging.sh: line 8: syntax error: unexpected end of file
Cabe señalar que, de forma predeterminada, cuando ejecuta el script, bash validará la sintaxis y arrojará estos errores incluso sin usar el modo noexec.
Alternativamente, también puede usar el set
comando o shebang para usar el noexec
modo.
set -n set -o noexec
O,
#! /bin/bash -n
Hay algunas herramientas externas que vale la pena echarle un vistazo para depurar los scripts. Una de esas herramientas es Shellcheck. . Shellcheck también se puede integrar con editores de texto populares como vscode, sublime text, Atom.
Conclusión
En este artículo, le mostré algunas de las formas de depurar scripts bash. A diferencia de otros lenguajes de programación, bash no tiene herramientas de depuración aparte de algunas opciones integradas. A veces, estas opciones de depuración integradas serán más que suficientes para realizar el trabajo.
Guías de secuencias de comandos de Bash:
- Bash Scripting:análisis de argumentos en Bash Scripts mediante getopts
- Cómo crear cuadros de diálogo GUI en secuencias de comandos Bash con Zenity en Linux y Unix
- Secuencias de comandos de Bash:declaración de caso
- Secuencias de comandos de Bash:declaraciones condicionales
- Bash Scripting:manipulación de cadenas
- Secuencias de comandos de Bash:comando Printf explicado con ejemplos
- Bash Scripting:matriz indexada explicada con ejemplos
- Secuencias de comandos de Bash:matriz asociativa explicada con ejemplos
- Secuencias de comandos de Bash:bucle For explicado con ejemplos
- Secuencias de comandos de Bash:ciclo while y till explicado con ejemplos
- Redireccionamiento de Bash explicado con ejemplos
- Secuencias de comandos de Bash:variables explicadas con ejemplos
- Bash Scripting:funciones explicadas con ejemplos
- Comando Bash Echo explicado con ejemplos en Linux
- Tutorial de Bash Heredoc para principiantes