¿Hay alguna forma de depurar un script bash? Por ejemplo, algo que imprime una especie de registro de ejecución como "línea de llamada 1", "línea de llamada 2", etc.
¿Hay alguna forma de depurar un script bash? Por ejemplo, algo que imprime una especie de registro de ejecución como "línea de llamada 1", "línea de llamada 2", etc.
Respuestas:
sh -x script [arg1 ...]
bash -x script [arg1 ...]
Estos le dan un rastro de lo que se está ejecutando. (Ver también 'Aclaración' cerca del final de la respuesta).
A veces, necesita controlar la depuración dentro del script. En ese caso, como me recordó Cheeto , puedes usar:
set -x
Esto activa la depuración. Luego puede apagarlo nuevamente con:
set +x
(Puede averiguar el estado de seguimiento actual analizando $-
, los indicadores actuales, para x
).
Además, los shells generalmente proporcionan opciones ' -n
' para 'no ejecución' y ' -v
' para modo 'detallado'; puede usarlos en combinación para ver si el shell cree que podría ejecutar su script, ocasionalmente útil si tiene una cita desequilibrada en alguna parte.
Existe la opinión de que la -x
opción ' ' en Bash es diferente de otras shells (ver los comentarios). El Manual de Bash dice:
-X
Imprima una traza de comandos simples, for
comandos, case
comandos, select
comandos y for
comandos aritméticos y sus argumentos o listas de palabras asociadas después de que se expanden y antes de que se ejecuten. El valor de la PS4
variable se expande y el valor resultante se imprime antes del comando y sus argumentos expandidos.
Eso no parece indicar un comportamiento diferente en absoluto. No veo ninguna otra referencia relevante a ' -x
' en el manual. No describe diferencias en la secuencia de inicio.
Aclaración : en sistemas como un cuadro típico de Linux, donde ' /bin/sh
' es un enlace simbólico a ' /bin/bash
' (o donde se encuentre el ejecutable Bash), las dos líneas de comando logran el efecto equivalente de ejecutar el script con el seguimiento de ejecución activado. En otros sistemas (por ejemplo, Solaris, y algunas variantes más modernas de Linux), /bin/sh
no es Bash, y las dos líneas de comando darían resultados (ligeramente) diferentes. En particular, ' /bin/sh
' estaría confundido por construcciones en Bash que no reconoce en absoluto. (En Solaris, /bin/sh
es un shell Bourne; en Linux moderno, a veces es Dash, un shell más pequeño, más estrictamente solo POSIX). Cuando se invoca por un nombre como este, la línea 'shebang' (' #!/bin/bash
' vs '#!/bin/sh
'
El manual de Bash tiene una sección sobre el modo Bash POSIX que, al contrario de una versión antigua pero errónea de esta respuesta (ver también los comentarios a continuación), describe en gran detalle la diferencia entre 'Bash invocado como sh
' y 'Bash invocado como bash
'.
Al depurar un script de shell (Bash), será sensato y sensato, incluso necesario, utilizar el shell nombrado en la línea shebang con la -x
opción. De lo contrario, puede (¿tendrá?) Obtener un comportamiento diferente al depurar al ejecutar el script.
bash
guión. ¡Y ejecutar un script bash con sh -x
hará que se comporte completamente diferente! Por favor actualice su respuesta.
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
He usado los siguientes métodos para depurar mi script.
set -e
hace que el script se detenga inmediatamente si algún programa externo devuelve un estado de salida distinto de cero. Esto es útil si su secuencia de comandos intenta manejar todos los casos de error y en caso de que no lo logre quedar atrapado.
set -x
fue mencionado anteriormente y es sin duda el más útil de todos los métodos de depuración.
set -n
También podría ser útil si desea verificar su secuencia de comandos en busca de errores de sintaxis.
strace
También es útil para ver qué está pasando. Especialmente útil si no ha escrito el guión usted mismo.
strace -f
es necesario si también desea encontrar errores en los procesos iniciados por el script. (lo que lo hace mucho más detallado, pero sigue siendo útil si lo limita a las llamadas al sistema que le interesan).
set -e
es ... controversiales .
Esta respuesta es válida y útil: https://stackoverflow.com/a/951352
Pero, encuentro que los métodos de depuración de script "estándar" son ineficientes, poco intuitivos y difíciles de usar. Para aquellos acostumbrados a depuradores de GUI sofisticados que ponen todo a su alcance y hacen que el trabajo sea fácil para problemas fáciles (y posibles para problemas difíciles), estas soluciones no son muy satisfactorias.
Lo que hago es usar una combinación de DDD y bashdb. El primero ejecuta el segundo, y el segundo ejecuta su script. Esto proporciona una interfaz de usuario de múltiples ventanas con la capacidad de recorrer el código en contexto y ver variables, apilar, etc., sin el esfuerzo mental constante de mantener el contexto en su cabeza o seguir enumerando la fuente.
Hay una guía para configurar eso aquí: http://ubuntuforums.org/showthread.php?t=660223
También puede escribir "set -x" dentro del script.
Encontré la utilidad Shellcheck y puede que algunas personas lo encuentren interesante https://github.com/koalaman/shellcheck
Un pequeño ejemplo:
$ cat test.sh
ARRAY=("hello there" world)
for x in $ARRAY; do
echo $x
done
$ shellcheck test.sh
In test.sh line 3:
for x in $ARRAY; do
^-- SC2128: Expanding an array without an index only gives the first element.
corrige el error, primero intenta ...
$ cat test.sh
ARRAY=("hello there" world)
for x in ${ARRAY[@]}; do
echo $x
done
$ shellcheck test.sh
In test.sh line 3:
for x in ${ARRAY[@]}; do
^-- SC2068: Double quote array expansions, otherwise they're like $* and break on spaces.
Intentemoslo de nuevo...
$ cat test.sh
ARRAY=("hello there" world)
for x in "${ARRAY[@]}"; do
echo $x
done
$ shellcheck test.sh
¡encuentra ahora!
Es solo un pequeño ejemplo.
Use eclipse con los complementos sin cáscara y basheclipse.
https://sourceforge.net/projects/shelled/?source=directory https://sourceforge.net/projects/basheclipse/?source=directory
Para descascarado: descargue el zip e impórtelo en eclipse a través de ayuda -> instale un nuevo software: archivo local Para basheclipse: copie los frascos en el directorio dropins de eclipse
Siga los pasos que proporciona https://sourceforge.net/projects/basheclipse/files/?source=navbar
Escribí un tutorial con muchas capturas de pantalla en http://dietrichschroff.blogspot.de/2017/07/bash-enabling-eclipse-for-bash.html
Construí un depurador Bash. Solo inténtalo. Espero que ayude https://sourceforge.net/projects/bashdebugingbash
set + x = @ECHO OFF, set -x = @ECHO ON.
Puede agregar una -xv
opción al Shebang estándar de la siguiente manera:
#!/bin/bash -xv
-x
: Muestra los comandos y sus argumentos a medida que se ejecutan.
-v
: Muestra las líneas de entrada del shell a medida que se leen.
ltrace
es otra utilidad de Linux similar a strace
. Sin embargo, ltrace
enumera todas las llamadas a la biblioteca que se llaman en un proceso ejecutable o en ejecución. Su nombre en sí proviene del rastreo de llamadas de biblioteca. Por ejemplo:
ltrace ./executable <parameters>
ltrace -p <PID>
Creo que puedes probar este depurador de Bash: http://bashdb.sourceforge.net/ .
set -[nvx]
Además de
set -x
y
set +x
para detener el volcado.
Me gustaría hablar sobre qué set -v
volcado es tan pequeño como un resultado menos desarrollado.
bash <<<$'set -x\nfor i in {0..9};do\n\techo $i\n\tdone\nset +x' 2>&1 >/dev/null|wc -l
21
for arg in x v n nx nv nvx;do echo "- opts: $arg"
bash 2> >(wc -l|sed s/^/stderr:/) > >(wc -l|sed s/^/stdout:/) <<eof
set -$arg
for i in {0..9};do
echo $i
done
set +$arg
echo Done.
eof
sleep .02
done
- opts: x
stdout:11
stderr:21
- opts: v
stdout:11
stderr:4
- opts: n
stdout:0
stderr:0
- opts: nx
stdout:0
stderr:0
- opts: nv
stdout:0
stderr:5
- opts: nvx
stdout:0
stderr:5
Para probar algunas variables, uso alguna vez esto:
bash <(sed '18ideclare >&2 -p var1 var2' myscript.sh) args
para añadir:
declare >&2 -p var1 var2
en la línea 18 y en funcionamiento resultante de la escritura (con args ), sin tener que modificarlas.
por supuesto, esto podría usarse para agregar set [+-][nvx]
:
bash <(sed '18s/$/\ndeclare -p v1 v2 >\&2/;22s/^/set -x\n/;26s/^/set +x\n/' myscript) args
se agregará declare -p v1 v2 >&2
después de la línea 18, set -x
antes de la línea 22 y set +x
antes de la línea 26.
bash <(sed '2,3s/$/\ndeclare -p LINENO i v2 >\&2/;5s/^/set -x\n/;7s/^/set +x\n/' <(
seq -f 'echo $@, $((i=%g))' 1 8)) arg1 arg2
arg1 arg2, 1
arg1 arg2, 2
declare -i LINENO="3"
declare -- i="2"
/dev/fd/63: line 3: declare: v2: not found
arg1 arg2, 3
declare -i LINENO="5"
declare -- i="3"
/dev/fd/63: line 5: declare: v2: not found
arg1 arg2, 4
+ echo arg1 arg2, 5
arg1 arg2, 5
+ echo arg1 arg2, 6
arg1 arg2, 6
+ set +x
arg1 arg2, 7
arg1 arg2, 8
Nota: ¡La preocupación $LINENO
se verá afectada por las modificaciones sobre la marcha !
(Para ver el script resultante sin ejecutar, simplemente suelte bash <(
y ) arg1 arg2
)
Echa un vistazo a mi respuesta sobre cómo perfilar scripts de bash
Hay una buena cantidad de detalles sobre el registro de scripts de shell a través de varaibles globales de shell. Podemos emular el tipo similar de inicio de sesión en el script de shell: http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html
La publicación tiene detalles sobre niveles de registro de introducción como INFO, DEBUG, ERROR. Detalles de seguimiento como entrada de script, salida de script, entrada de función, salida de función.
Registro de muestra: