Existe alguma maneira de depurar um script bash? Por exemplo, algo que imprime um tipo de log de execução como "linha de chamada 1", "linha de chamada 2" etc.
Existe alguma maneira de depurar um script bash? Por exemplo, algo que imprime um tipo de log de execução como "linha de chamada 1", "linha de chamada 2" etc.
Respostas:
sh -x script [arg1 ...]
bash -x script [arg1 ...]
Eles fornecem um rastro do que está sendo executado. (Veja também 'Esclarecimento' na parte inferior da resposta.)
Às vezes, você precisa controlar a depuração no script. Nesse caso, como Cheeto me lembrou , você pode usar:
set -x
Isso ativa a depuração. Você pode desativá-lo novamente com:
set +x
(Você pode descobrir o estado de rastreamento atual analisando $-
os sinalizadores atuais para x
.)
Além disso, os shells geralmente fornecem opções ' -n
' para 'sem execução' e ' -v
' para o modo 'detalhado'; você pode usá-los em conjunto para verificar se o shell pensa que poderia executar seu script - ocasionalmente útil se você tiver uma cotação desequilibrada em algum lugar.
Há alegações de que a -x
opção ' ' no Bash é diferente de outros shells (veja os comentários). O Manual do Bash diz:
-x
Imprima um rastro de comandos simples, for
comandos, case
comandos, select
comandos e for
comandos aritméticos e seus argumentos ou listas de palavras associadas depois de expandidos e antes de serem executados. O valor da PS4
variável é expandido e o valor resultante é impresso antes do comando e de seus argumentos expandidos.
Isso parece não indicar um comportamento diferente. Não vejo outras referências relevantes para ' -x
' no manual. Não descreve diferenças na sequência de inicialização.
Esclarecimento : Em sistemas como uma caixa típica do Linux, onde ' /bin/sh
' é um link simbólico para ' /bin/bash
' (ou onde quer que o executável Bash seja encontrado), as duas linhas de comando atingem o efeito equivalente de executar o script com o rastreamento de execução ativado. Em outros sistemas (por exemplo, Solaris, e algumas variantes mais modernas do Linux), /bin/sh
não é o Bash, e as duas linhas de comando dariam resultados (ligeiramente) diferentes. Mais notavelmente ' /bin/sh
', seria confundido por construções no Bash que ele não reconhece. (No Solaris, /bin/sh
é um shell Bourne; no Linux moderno, às vezes é Dash - um shell menor, mais estritamente apenas para POSIX.) Quando invocado por nome como esse, a linha 'shebang' ( #!/bin/bash
'vs '#!/bin/sh
'
O manual do Bash possui uma seção no modo Bash POSIX que, ao contrário de uma versão antiga e errônea desta resposta (veja também os comentários abaixo), descreve detalhadamente a diferença entre 'Bash chamado como sh
' e 'Bash chamado como bash
'
Ao depurar um script de shell (Bash), será sensato e sensato - até necessário - usar o shell nomeado na linha shebang com a -x
opção Caso contrário, você poderá (obterá) comportamento diferente ao depurar e ao executar o script.
bash
script. A execução de um script bash sh -x
fará com que ele se comporte completamente diferente! Atualize sua resposta.
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
Eu usei os seguintes métodos para depurar meu script.
set -e
faz o script parar imediatamente se algum programa externo retornar um status de saída diferente de zero. Isso é útil se o seu script tentar lidar com todos os casos de erro e onde uma falha nesse procedimento deve ser interceptada.
set -x
foi mencionado acima e é certamente o mais útil de todos os métodos de depuração.
set -n
também pode ser útil se você deseja verificar se há erros de sintaxe no seu script.
strace
também é útil para ver o que está acontecendo. Especialmente útil se você não tiver escrito o script.
strace -f
é necessário se você também deseja encontrar erros nos processos iniciados pelo script. (o que o torna muitas vezes mais detalhado, mas ainda útil se você o limitar aos syscalls nos quais está interessado).
set -e
é ... controverso .
Esta resposta é válida e útil: https://stackoverflow.com/a/951352
Mas acho que os métodos de depuração de script "padrão" são ineficientes, não intuitivos e difíceis de usar. Para aqueles acostumados a depuradores sofisticados de GUI que colocam tudo ao seu alcance e facilitam o trabalho para problemas fáceis (e possíveis para problemas difíceis), essas soluções não são muito satisfatórias.
O que faço é usar uma combinação de DDD e bashdb. O primeiro executa o último e o último executa seu script. Isso fornece uma interface do usuário com várias janelas com a capacidade de percorrer o código no contexto e exibir variáveis, pilha, etc., sem o esforço mental constante para manter o contexto em sua cabeça ou continuar listando novamente a fonte.
Há orientações sobre como configurar isso aqui: http://ubuntuforums.org/showthread.php?t=660223
Você também pode escrever "set -x" dentro do script.
Encontrei o utilitário shellcheck e algumas pessoas acham interessante https://github.com/koalaman/shellcheck
Um pequeno exemplo:
$ 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.
corrigir o erro, primeira tentativa ...
$ 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.
Vamos tentar de novo...
$ cat test.sh
ARRAY=("hello there" world)
for x in "${ARRAY[@]}"; do
echo $x
done
$ shellcheck test.sh
encontre agora!
É apenas um pequeno exemplo.
Use eclipse com os plugins com shell e basheclipse.
https://sourceforge.net/projects/shelled/?source=directory https://sourceforge.net/projects/basheclipse/?source=directory
Para shell: Faça o download do zip e importe-o para o eclipse via ajuda -> instalar novo software: arquivo local Para o basheclipse: Copie os frascos no diretório dropins do eclipse
Siga as etapas fornecidas https://sourceforge.net/projects/basheclipse/files/?source=navbar
Eu escrevi um tutorial com muitas capturas de tela em http://dietrichschroff.blogspot.de/2017/07/bash-enabling-eclipse-for-bash.html
Eu construí um depurador Bash. Apenas tente. Espero que ajude https://sourceforge.net/projects/bashdebugingbash
ajuste + x = @ECHO OFF, ajuste -x = @ECHO ON.
Você pode adicionar uma -xv
opção ao Shebang padrão da seguinte maneira:
#!/bin/bash -xv
-x
: Exibe comandos e seus argumentos à medida que são executados.
-v
: Exibe as linhas de entrada do shell conforme são lidas.
ltrace
é outro utilitário Linux semelhante ao strace
. No entanto, ltrace
lista todas as chamadas de biblioteca que estão sendo chamadas em um processo executável ou em execução. Seu próprio nome vem do rastreamento de chamadas de biblioteca. Por exemplo:
ltrace ./executable <parameters>
ltrace -p <PID>
Eu acho que você pode tentar este depurador Bash: http://bashdb.sourceforge.net/ .
set -[nvx]
Além de
set -x
e
set +x
para parar o despejo.
Eu gostaria de falar sobre set -v
qual despejo é menor e menos desenvolvido.
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 testar algumas variáveis, eu uso em algum momento isso:
bash <(sed '18ideclare >&2 -p var1 var2' myscript.sh) args
para adicionar:
declare >&2 -p var1 var2
na linha 18 e executando o script resultante (com args ), sem precisar editá-los.
claro, isso poderia ser usado para adicionar set [+-][nvx]
:
bash <(sed '18s/$/\ndeclare -p v1 v2 >\&2/;22s/^/set -x\n/;26s/^/set +x\n/' myscript) args
adicionará declare -p v1 v2 >&2
após a linha 18, set -x
antes da linha 22 e set +x
antes da linha 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: Os cuidados $LINENO
serão afetados por modificações on-the-fly !
(Para ver o script resultante sem execução, basta soltar bash <(
e ) arg1 arg2
)
Dê uma olhada na minha resposta sobre como criar perfis de bash scripts
Há uma boa quantidade de detalhes no registro de scripts de shell por meio de variáveis globais do shell. Podemos emular o tipo semelhante de log no shell script: http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html
A postagem tem detalhes sobre a introdução de níveis de log como INFO, DEBUG, ERROR. Detalhes de rastreamento, como entrada de script, saída de script, entrada de função, saída de função.
Exemplo de log: