Como depurar um script bash?

Existe alguma maneira de depurar um script bash? Por exemplo, algo que imprime uma espécie de log de execução como “linha de chamada 1”, “linha de chamada 2” etc.

sh -x script [arg1 ...] bash -x script [arg1 ...] 

Estes lhe dão um rastreamento do que está sendo executado. (Veja também “Esclarecimento” na parte inferior da resposta.)

Às vezes, você precisa controlar a debugging no script. Nesse caso, como Cheeto me lembrou , você pode usar:

 set -x 

Isso ativa a debugging. Você pode desligá-lo novamente com:

 set +x 

(Você pode descobrir o estado atual do rastreamento analisando $- , os sinalizadores atuais, para x .)

Além disso, os shells geralmente fornecem opções ‘ -n ‘ para ‘no execution’ e ‘ -v ‘ para o modo ‘verbose’; você pode usá-los em combinação para ver se o shell acha que poderia executar seu script – ocasionalmente útil se você tiver uma cotação desbalanceada em algum lugar.


Existe uma alegação de que a opção ‘ -x ‘ no Bash é diferente de outras shells (veja os comentários). O manual do bash diz:

  • -x

    Imprima um rastreamento de comandos simples, for comandos, comandos de case , comandos de select e aritmética for comandos e seus argumentos ou listas de palavras associadas depois que eles forem expandidos e antes de serem executados. O valor da variável PS4 é expandido e o valor resultante é impresso antes do comando e seus argumentos expandidos.

Isso não parece indicar comportamento diferente. Não vejo outras referências relevantes para ‘ -x ‘ no manual. Não descreve as diferenças na sequência de boot.

Esclarecimento : Em sistemas como uma típica checkbox 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 alcançam o efeito equivalente de executar o script com o rastreamento de execução. Em outros sistemas (por exemplo, Solaris e algumas variantes mais modernas do Linux), o /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 de todo. (No Solaris, o /bin/sh é um shell Bourne; no Linux moderno, às vezes é Dash – um shell menor e mais estritamente POSIX.) Quando invocado por nome como esse, a linha ‘shebang’ (‘ #!/bin/bash ‘vs '#!/bin/sh ‘) no início do arquivo não tem efeito sobre como o conteúdo é interpretado.

O manual do Bash tem uma seção sobre o modo Bash POSIX que, ao contrário de uma versão antiga, mas errônea, desta resposta (veja também os comentários abaixo), descreve detalhadamente a diferença entre ‘Bash invocado como sh ‘ e ‘Bash invocado como bash ‘.

Ao depurar um script de shell (Bash), será sensato e são – até mesmo necessário – usar o shell nomeado na linha shebang com a opção -x . Caso contrário, você pode (será?) Obter um comportamento diferente ao depurar ao executar o script.

Eu usei os seguintes methods 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 script tentar lidar com todos os casos de erro e se houver uma falha em fazê-lo.

set -x foi mencionado acima e é certamente o mais útil de todos os methods de debugging.

set -n também pode ser útil se você quiser verificar seu script para erros de syntax.

strace também é útil para ver o que está acontecendo. Especialmente útil se você não tiver escrito o roteiro sozinho.

Essa resposta é válida e útil: https://stackoverflow.com/a/951352

Mas, acho que os methods de debugging de script “padrão” são ineficientes, não intuitivos e difíceis de usar. Para aqueles acostumados a depuradores de GUI sofisticados 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 eu faço é usar uma combinação de DDD e bashdb. O primeiro executa o último e o segundo executa o seu script. Isso fornece uma interface do usuário com várias janelas com a capacidade de percorrer o código no contexto e visualizar variables, pilha, etc., sem o esforço mental constante de manter o contexto em sua mente ou manter listando novamente a origem.

Há orientações sobre como configurar isso aqui: http://ubuntuforums.org/showthread.php?t=660223

Você também pode escrever “set -x” no script.

Eu encontrei o utilitário shellcheck e pode ser que algumas pessoas achem 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. 

corrija o bug, primeiro tente …

 $ 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 

ache agora!

É apenas um pequeno exemplo.

Eu construí um depurador Bash. Apenas experimente. Espero que ajude https://sourceforge.net/projects/bashdebugingbash

Instale o VSCode , adicione a extensão de debugging bash e você estará pronto para depurar no modo visual. veja aqui em ação.

insira a descrição da imagem aqui

Eu acho que você pode tentar este depurador Bash: http://bashdb.sourceforge.net/ .

set + x = @ ECHO OFF, set -x = @ ECHO ON.


Você pode adicionar a opção -xv ao Shebang padrão da seguinte maneira:

 #!/bin/bash -xv 

-x : Exibe comandos e seus argumentos conforme são executados.
-v : Exibe as linhas de input do shell à medida que são lidas.


ltrace é outro utilitário do Linux semelhante ao strace . No entanto, o ltrace lista todas as chamadas da biblioteca que estão sendo chamadas em um processo executável ou em execução. Seu próprio nome vem do rastreamento de chamada de biblioteca. Por exemplo:

 ltrace ./executable  ltrace -p  

Fonte

Algum truque para depurar scripts bash :

Usando set -[nvx]

Além de

 set -x 

e

 set +x 

para parar o lixo.

Eu gostaria de falar sobre o set -v que despeja tão menor quanto menos saída desenvolvida.

 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 xvn nx nv nvx;do echo "- opts: $arg" bash 2> >(wc -l|sed s/^/stderr:/) > >(wc -l|sed s/^/stdout:/) < 

Dump variables ​​ou rastreamento na mosca

Para testar algumas variables, 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 

irá adicionar declare -p v1 v2 >&2 após a linha 18, set -x antes da linha 22 e set +x antes da linha 26.

pequena amostra:

 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: O cuidado com o $LINENO será afetado por modificações imediatas !

(Para ver o script resultante sem executar, simplesmente solte bash <( e ) arg1 arg2 )

Passo a passo, tempo de execução

Dê uma olhada na minha resposta sobre como fazer o perfil de scripts bash

Use o eclipse com os plugins shelled & 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 basheclipse: Copie o diretório jars into dropins do eclipse

Siga as etapas em https://sourceforge.net/projects/basheclipse/files/?source=navbar

insira a descrição da imagem aqui

Eu escrevi um tutorial com muitas imagens em http://dietrichschroff.blogspot.de/2017/07/bash-enabling-eclipse-for-bash.html

Há uma boa quantidade de detalhes no registro de scripts de shell por meio de variables ​​globais de shell. Podemos emular o tipo semelhante de registro em shell script: http://www.cubicrace.com/2016/03/log-tracing-mechnism-for-shell-scripts.html

A postagem contém detalhes sobre a introdução de níveis de log como INFO, DEBUG e ERROR. Detalhes de rastreamento, como input de script, saída de script, input de function, saída de function.

Log de amostra:

insira a descrição da imagem aqui

Escrever mensagens de erro Bash

Um script de shell é um arquivo de texto contendo comandos shell. Por padrão, o interpretador de comandos e os utilitários de shell exibem mensagens de erro e diagnóstico no erro padrão. O Bash pode imprimir um rastreamento de um script em execução se a opção xtrace estiver ativada.

O código de amostra imprime um rastreio no arquivo xtrace.log . A function err permite identificar rapidamente mensagens de erro. O rastreio da function err é cancelado porque seu ambiente de execução atual oculta informações que formam o rastreio.

Código de amostra

 #!/bin/bash set -o errexit exec 4>xtrace.log BASH_XTRACEFD=4 PS4='+ $0: line ${LINENO}: ${FUNCNAME[0]}: ' set -x echo 'Welcome!' err() { printf "%s\n" "${BASH_SOURCE[0]}: line ${BASH_LINENO[0]}: ${FUNCNAME[0]}: $1" } >&2 4>/dev/null err 'oops, something went wrong!' 

Este código de exemplo gera:

 prompt% my_script Welcome ./my_script: line 16: err: oops, something went wrong! 

Mensagens de erro podem ser redirecionadas para ocultar mensagens de diagnóstico:

 prompt% my_script 2>err.log Welcome! 

Observe o rastreio e observe as mensagens de erro e diagnóstico:

 prompt% cat xtrace.log + ./my_script: line 9: main: echo Welcome + ./my_script: line 16: main: err 'oops, something went wrong!' prompt% cat err.log ./my_script: line 16: err: oops, something went wrong!