Qual é o seu método favorito para lidar com erros no Bash? O melhor exemplo de manipulação de erros que encontrei na Web foi escrito por William Shotts, Jr em http://www.linuxcommand.org .
Ele sugere usar a seguinte função para tratamento de erros no Bash:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
Você tem uma rotina de tratamento de erros melhor que você usa nos scripts Bash?
Respostas:
Use uma armadilha!
... então, sempre que você criar um arquivo temporário:
e
$temp_foo
será excluído na saída, e o número da linha atual será impresso. (set -e
também fornecerá um comportamento de saída com erro, embora venha com sérias advertências e diminua a previsibilidade e portabilidade do código).Você pode deixar a interceptação chamar
error
por você (nesse caso, ele usa o código de saída padrão 1 e nenhuma mensagem) ou chamá-lo você mesmo e fornecer valores explícitos; por exemplo:sairá com o status 2 e fornecerá uma mensagem explícita.
fonte
Essa é uma boa solução. Eu só queria adicionar
como um mecanismo de erro rudimentar. Parará imediatamente o seu script se um comando simples falhar. Eu acho que esse deveria ter sido o comportamento padrão: como esses erros quase sempre significam algo inesperado, não é realmente sensato continuar executando os seguintes comandos.
fonte
set -e
não é sem truques : Veja mywiki.wooledge.org/BashFAQ/105 para vários.set -o pipefail
set -e
tem uma alta relação custo-benefício.set -e
mim mesmo, mas vários outros usuários regulares do irc.freenode.org # bash aconselham (em termos bastante fortes) contra isso. No mínimo, as dicas em questão devem ser bem compreendidas.Ler todas as respostas desta página me inspirou muito.
Então, aqui está a minha dica:
conteúdo do arquivo: lib.trap.sh
Exemplo de uso:
conteúdo do arquivo: trap-test.sh
Corrida:
Resultado:
Como você pode ver na captura de tela abaixo, a saída é colorida e a mensagem de erro é exibida no idioma usado.
fonte
test ${#g_libs[@]} == 0
não é compatível com POSIX (o teste POSIX suporta=
comparações de cadeias ou comparações-eq
numéricas, mas não==
, sem mencionar a falta de matrizes no POSIX) e, se você não está tentando ser compatível com POSIX, por que, no mundo você está usandotest
em vez de um contexto de matemática?(( ${#g_libs[@]} == 0 ))
é, afinal, mais fácil de ler.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Uma alternativa equivalente a "set -e" é
Isso torna o significado da bandeira um pouco mais claro do que apenas "-e".
Adição aleatória: para desativar temporariamente o sinalizador e retornar ao padrão (de execução contínua, independentemente dos códigos de saída), basta usar
Isso impede o tratamento adequado de erros mencionado em outras respostas, mas é rápido e eficaz (assim como o bash).
fonte
$(foo)
em uma linha simples, em vez de apenasfoo
é geralmente a coisa errada. Por que promovê-lo, dando-o como um exemplo?Inspirado pelas idéias apresentadas aqui, desenvolvi uma maneira legível e conveniente de lidar com erros nos scripts do bash no meu projeto do bash clichê .
Simplesmente fornecendo a biblioteca, você obtém o seguinte da caixa (isto é, interromperá a execução de qualquer erro, como se estivesse usando
set -e
graças a umtrap
onERR
e alguns bash-fu ):Existem alguns recursos extras que ajudam a lidar com erros, como try and catch ou a palavra-chave throw , que permitem interromper a execução em um ponto para ver o backtrace. Além disso, se o terminal o suportar, ele cospe emojis de linha de força, colore partes da saída para melhor legibilidade e enfatize o método que causou a exceção no contexto da linha de código.
A desvantagem é - não é portátil - o código funciona no bash, provavelmente> = 4 (mas eu imagino que poderia ser portado com algum esforço para o bash 3).
O código é separado em vários arquivos para melhor manuseio, mas eu fui inspirado pela idéia de backtrace da resposta acima de Luca Borrione .
Para ler mais ou dar uma olhada na fonte, consulte GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
fonte
Prefiro algo realmente fácil de ligar. Então, eu uso algo que parece um pouco complicado, mas é fácil de usar. Normalmente, copio e colo o código abaixo nos meus scripts. Uma explicação segue o código.
Eu costumo fazer uma chamada para a função de limpeza ao lado da função error_exit, mas isso varia de script para script, então eu a deixei de fora. As armadilhas captam os sinais de terminação comuns e garantem que tudo seja limpo. O apelido é o que faz a mágica real. Eu gosto de verificar tudo quanto a falhas. Então, em geral, eu chamo de programas em um "se!" declaração de tipo. Subtraindo 1 do número da linha, o alias me dirá onde ocorreu a falha. Também é muito simples de ligar e praticamente à prova de idiotas. Abaixo está um exemplo (basta substituir / bin / false pelo que você quiser chamar).
fonte
$LINENO - 1
. Mostrar corretamente sem ele.false || die "hello death"
Outra consideração é o código de saída a ser retornado. Apenas "
1
" é bastante padrão, embora exista um punhado de códigos de saída reservados que o bash usa , e a mesma página argumenta que os códigos definidos pelo usuário devem estar no intervalo 64-113 para estar em conformidade com os padrões C / C ++.Você também pode considerar a abordagem de vetor de bits
mount
usada para seus códigos de saída:OR
- Ao juntar os códigos, seu script sinaliza vários erros simultâneos.fonte
Eu uso o seguinte código de interceptação, ele também permite que os erros sejam rastreados através de pipes e comandos 'time'
fonte
function
palavra-chave é incompatível com POSIX gratuitamente. Considere fazer sua declaração apenaserror() {
, semfunction
antes.${$?}
deveria ser apenas$?
, ou${?}
se você insistir em usar aparelhos desnecessários; o interior$
está errado.Eu usei
antes; Eu acho que porque 'exit' estava falhando para mim por algum motivo. Os padrões acima parecem uma boa idéia, no entanto.
fonte
Isso me serviu bem por um tempo agora. Ele imprime mensagens de erro ou aviso em vermelho, uma linha por parâmetro e permite um código de saída opcional.
fonte
Não tenho certeza se isso será útil para você, mas modifiquei algumas das funções sugeridas aqui para incluir a verificação do erro (código de saída do comando anterior) dentro dele. Em cada "verificação", também passo como parâmetro a "mensagem" sobre qual é o erro para fins de registro.
Agora, para chamá-lo no mesmo script (ou em outro, se eu usar
export -f error_exit
), basta escrever o nome da função e passar uma mensagem como parâmetro, assim:Usando isso, eu consegui criar um arquivo bash realmente robusto para algum processo automatizado e ele será interrompido em caso de erros e me notificará (
log.sh
fará isso)fonte
function
palavra-chave, apenaserror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Esse truque é útil para a falta de comandos ou funções. O nome da função ausente (ou executável) será passado em $ _
fonte
$_
estaria disponível na função o mesmo que$?
? Não sei se há algum motivo para usar um na função, mas não o outro.Essa função tem me servido muito bem recentemente:
Você o chama anexando 0 ou o último valor de retorno ao nome do comando a ser executado, para poder encadear comandos sem precisar verificar valores de erro. Com isso, esse bloco de instruções:
Torna-se isso:
Se algum dos comandos falhar, o código de erro é simplesmente passado para o final do bloco. Acho útil quando você não deseja que comandos subsequentes sejam executados se um anterior falhar, mas também não deseja que o script saia imediatamente (por exemplo, dentro de um loop).
fonte
Usar armadilha nem sempre é uma opção. Por exemplo, se você estiver escrevendo algum tipo de função reutilizável que precise de tratamento de erros e possa ser chamada de qualquer script (depois de buscar o arquivo com funções auxiliares), essa função não poderá assumir nada sobre o horário de saída do script externo, o que dificulta o uso de armadilhas. Outra desvantagem do uso de traps é a composição ruim, pois você corre o risco de substituir o traps anterior que pode ser configurado anteriormente na cadeia de chamadas.
Há um pequeno truque que pode ser usado para lidar adequadamente com erros sem traps. Como você já deve saber de outras respostas,
set -e
não funciona dentro de comandos se você usar o||
operador após eles, mesmo que você os execute em um subshell; por exemplo, isso não funcionaria:Mas o
||
operador é necessário para impedir o retorno da função externa antes da limpeza. O truque é executar o comando interno em segundo plano e esperar imediatamente por ele. Owait
builtin retornará o código de saída do comando interno, e agora você está usando||
depoiswait
, não a função interna, portantoset -e
funciona corretamente dentro do último:Aqui está a função genérica que se baseia nessa idéia. Ele deve funcionar em todos os shells compatíveis com POSIX se você remover
local
palavras-chave, ou seja, substitua todoslocal x=y
por apenasx=y
:Exemplo de uso:
Executando o exemplo:
A única coisa que você precisa estar ciente ao usar esse método é que todas as modificações das variáveis do Shell feitas a partir do comando para o qual você passa
run
não serão propagadas para a função de chamada, porque o comando é executado em um subshell.fonte