Existe uma maneira de ter uma função na execução automática do meu script bash em qualquer erro de comando?

10

Eu estou escrevendo um script de shell que precisa fazer um monte de comandos e cada comando depende de todos os comandos anteriores. Se algum comando falhar, o script inteiro falhará e eu chamo uma função de saída. Eu poderia verificar o código de saída de cada comando, mas estou querendo saber se existe um modo que eu possa ativar ou uma maneira de obter bash para fazer isso automaticamente.

Por exemplo, tome estes comandos:

cd foo || myfunc
rm a || myfunc
cd bar || myfunc
rm b || myfunc


Existe uma maneira que eu possa de alguma forma sinalizar para o shell antes de executar esses comandos que ele deve chamar myfunc se algum deles falhar, para que em vez disso eu possa escrever algo mais limpo como:

cd foo
rm a
cd bar
rm b
    
por test 11.12.2014 / 06:42

3 respostas

12

Você pode usar o bash armadilha ERR para fazer com que seu script seja encerrado se algum comando retornar status maior que zero e executar sua função ao sair.

Algo como:

myfunc() {
  echo 'Error raised. Exiting!'
}

trap 'myfunc' ERR

# file does not exist, causing error
ls asd
echo 123

Note que bash trap ERR implícito set -o errexit ou set -e e não é POSIX.

E o ERR trap não será executado se o comando com falha fizer parte da lista de comandos imediatamente após until ou while keyword, parte do teste seguindo as palavras if ou elif reservada, parte de um comando executado em && ou || list, ou se o status de retorno do comando estiver sendo invertido usando ! .

    
por 11.12.2014 / 06:54
1

Uma variação (talvez) mais simples da resposta aceita:

  1. Use set -e para causar a falha de um comando para abortar a execução de uma lista.
  2. Basta listar seus comandos.
  3. Use uma declaração if - then - else para executar seu (s) comando (s) de manipulação de erros. Esta última peça é um pouco complicada. Assista:
set -e
if
    cmd1                        # e.g.,     cd foo
    cmd2                        # e.g.,     rm a
    cmd3                        # e.g.,     cd bar
    cmd4                        # e.g.,     rm b
then
    set +e
    commands to do upon success (if any)
else
    set +e
    myfunc
    other commands to do upon failure (if any)
fi

A parte complicada é que você coloca seus comandos para a if part da if - then - else , não a parte then ou a parte else . Lembre-se de que a sintaxe da declaração if é

if list; then list; [ elif list; then list; ] ... [ else list; ] fi
   ↑↑↑↑
O set -e diz ao shell que, se cmd1 ( cd foo ) falhar, não deve continuar e executar cmd2 (%código%), e assim por diante abaixo da linha. Se isso aconteceu com um comando no nível mais externo de um script de shell, a casca sairia. No entanto, como o rm a · cmd1 · cmd2 · cmd3 é uma lista (composta) após um cmd4 , o fracasso de qualquer um desses quatro comandos simplesmente faz com que toda a lista falhe - que faz com que a cláusula if seja executada. Se todos os quatro comandos forem bem-sucedidos, a cláusula else será executada.

Em ambos os casos, a primeira coisa que você deve fazer é provavelmente desabilitar (desative) a opção then fazendo e . Caso contrário, o roteiro pode ser expelido da água se um comando em set +e falhar.

myfunc é especificado e descrito na especificação POSIX .

    
por 09.01.2017 / 20:28
0

Tire sua palavra " cada comando depende de cada comando anterior. Se algum comando falhar, o script inteiro deve falhar " literalmente, acho que você não precisa de nenhuma função especial para tratar os erros. / p>

Tudo o que você precisa é encadear seus comandos com o operador && e o operador || , que faz exatamente o que você escreveu.

Por exemplo, essa cadeia quebrará e imprimirá "algo deu errado" se qualquer dos comandos anteriores quebrou (o bash lê da esquerda para a direita)

cd foo && rm a && cd bar && rm b || echo "something went wrong"

Exemplo real (criei dir foo, arquivo a, dir bar e arquivo b apenas para uma demonstração real):

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm bb || echo "something is wrong"
rm: cannot remove 'bb': No such file or directory
something is wrong #mind the error in the last command

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm aa && cd bar && rm b || echo "something is wrong"
rm: cannot remove 'aa': No such file or directory
something is wrong #mind the error in second command in the row

E finalmente, se todos os comandos foram executados com sucesso (código de saída 0), o script simplesmente continua:

gv@debian:/home/gv/Desktop/PythonTests$ cd foo && rm a && cd bar && rm b || echo "something is wrong"
gv@debian:/home/gv/Desktop/PythonTests/foo/bar$ 
# mind that the error message is not printed since all commands were successful.

O que é importante lembrar é que, com o uso de & & O próximo comando é executado se o comando anterior sair com o código 0, o que para bash significa sucesso.

Se algum comando der errado na cadeia, o comando / script / o que vem depois || será executado.

E apenas para o registro, se você precisar realizar ações diferentes, dependendo do comando que quebrou, você também pode fazê-lo com script clássico, monitorando o valor de $? , que informa o código de saída do comando exatamente anterior ( retorna zero se o comando for executado com sucesso ou outro número positivo se o comando falhar)

Exemplo:

for comm in {"cd foo","rm a","cd bbar","rm b"};do  #mind the error in third command
eval $comm
    if [[ $? -ne 0 ]];then 
        echo "something is wrong in command $comm"
        break
    else 
    echo "command $comm executed succesful"
    fi
done

Saída:

command cd foo executed succesfull
command rm a executed succesfull
bash: cd: bbar: No such file or directory
something is wrong in command cd bbar

Dica: você pode suprimir a mensagem "bash: cd: bbar: Nenhum tal arquivo ..." aplicando eval $comm 2>/dev/null

    
por 09.01.2017 / 22:46