O que set -e significa em um script bash?


713

Estou estudando o conteúdo deste arquivo preinst que o script executa antes que o pacote seja descompactado do seu arquivo Debian (.deb).

O script tem o seguinte código:

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

Minha primeira consulta é sobre a linha:

set -e

Eu acho que o restante do script é bastante simples: verifica se o gerenciador de pacotes Debian / Ubuntu está executando uma operação de instalação. Se for, verifica se meu aplicativo acabou de ser instalado no sistema. Se houver, o script imprimirá a mensagem "MyApplicationName acabou de instalar" e termina ( return 1significa que termina com um "erro", não é?).

Se o usuário solicitar ao sistema de pacotes Debian / Ubuntu que instale meu pacote, o script também excluirá dois diretórios.

Isso está certo ou estou faltando alguma coisa?



46
motivo pelo qual você não conseguiu encontrar isso no google: -e na sua consulta é interpretado como negação. Tente a seguinte consulta: bash set "-e"
Maleev

3
@twalberg Quando eu me perguntava a mesma pergunta, eu estava olhandoman set
Sedat Kilinc

4
se você estiver procurando como desativá-lo, troque o traço para um prefixo positivo:set +e
Tom Saleeba

@twalberg, mas perguntar a pessoas reais é muito mais interessante do que apenas fazer um pedido a um robô ;-).
Vdegenne 29/04

Respostas:


797

De help set:

  -e  Exit immediately if a command exits with a non-zero status.

Mas isso é considerado uma prática ruim para alguns (autores de perguntas frequentes sobre o bash FAQ e irc freenode #bash FAQ). É recomendável usar:

trap 'do_something' ERR

para executar a do_somethingfunção quando ocorrerem erros.

Veja http://mywiki.wooledge.org/BashFAQ/105


14
Qual seria o do_something se eu quisesse a mesma semântica de "Sair imediatamente se um comando sair com um status diferente de zero"?
CMCDragonkai

71
trap 'exit' ERR
Chepner # 28/14

12
A ERRarmadilha não é herdada pelas funções do shell, portanto, se você tiver funções, set -o errtraceou set -Epermitirá que você defina a armadilha apenas uma vez e a aplique globalmente.
ykay 17/07/2015

31
não trap 'exit' ERRfazer qualquer coisa diferente set -e?
Andy

22
se é uma prática ruim, por que é usado nos pacotes Debian ?
Phuclv

98

set -einterrompe a execução de um script se um comando ou pipeline tiver um erro - que é o oposto do comportamento padrão do shell, que é ignorar erros nos scripts. Digite help setum terminal para ver a documentação deste comando interno.


45
Ele interrompe a execução apenas se o último comando em um pipeline apresentar um erro. Há uma opção específica do Bash, set -o pipefailque pode ser usada para propagar erros para que o valor de retorno do comando pipeline seja diferente de zero se um dos comandos anteriores sair com um status diferente de zero.
Anthony Geoghegan

2
Lembre-se de que isso -o pipefailsignifica apenas que o status de saída do primeiro -o errexitcomando diferente de zero (ou seja, com erro em termos) do pipeline é propagado até o fim. Os comandos restantes no pipeline ainda são executados , mesmo com set -o errexit. Por exemplo: echo success | cat - <(echo piping); echo continues, onde echo successrepresenta um, mas de comando bem-sucedido falível, imprimirá success, pipinge continues, mas false | cat - <(echo piping); echo continues, com falserepresentando o comando agora erroring silenciosamente, ainda será impresso pipingantes de sair.
Bb010g 23/08/19

55

Conforme bash - o manual Set Builtin , se -e/ errexitfor definido, o shell será encerrado imediatamente se um pipeline que consiste em um único comando simples , uma lista ou um comando composto retornar um status diferente de zero.

Por padrão, o status de saída de um pipeline é o status de saída do último comando no pipeline, a menos que a pipefailopção esteja ativada (está desativada por padrão).

Nesse caso, o status de retorno do pipeline do último comando (mais à direita) para sair com um status diferente de zero ou zero se todos os comandos forem encerrados com êxito.

Se você deseja executar algo na saída, tente definir trap, por exemplo:

trap onexit EXIT

onde onexitestá sua função de fazer algo na saída, como abaixo, que está imprimindo o rastreamento de pilha simples :

onexit(){ while caller $((n++)); do :; done; }

Existe uma opção semelhante -E/errtrace que trava no ERR, por exemplo:

trap onerr ERR

Exemplos

Exemplo de status zero:

$ true; echo $?
0

Exemplo de status diferente de zero:

$ false; echo $?
1

Negando exemplos de status:

$ ! false; echo $?
0
$ false || true; echo $?
0

Teste com pipefaila desativação:

$ bash -c 'set +o pipefail -e; true | true | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; false | false | true; echo success'; echo $?
success
0
$ bash -c 'set +o pipefail -e; true | true | false; echo success'; echo $?
1

Teste com pipefaila ativação:

$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1

55

Encontrei esta postagem enquanto tentava descobrir qual era o status de saída para um script que foi interrompido devido a um set -e. A resposta não me pareceu óbvia; daí esta resposta. Basicamente, set -einterrompe a execução de um comando (por exemplo, um script de shell) e retorna o código de status de saída do comando que falhou (ou seja, o script interno, não o script externo) .

Por exemplo, suponha que eu tenha o script de shell outer-test.sh:

#!/bin/sh
set -e
./inner-test.sh
exit 62;

O código para inner-test.shé:

#!/bin/sh
exit 26;

Quando corro outer-script.shda linha de comando, meu script externo termina com o código de saída do script interno:

$ ./outer-test.sh
$ echo $?
26

10

Acredito que a intenção é que o script em questão falhe rapidamente.

Para testar você mesmo, basta digitar set -eem um prompt do bash. Agora, tente correr ls. Você receberá uma listagem de diretório. Agora digite lsd. Esse comando não é reconhecido e retornará um código de erro; portanto, seu prompt do bash será fechado (devido a set -e).

Agora, para entender isso no contexto de um 'script', use este script simples:

#!/bin/bash 
# set -e

lsd 

ls

Se você executá-lo como está, obterá a lista de diretórios lsna última linha. Se você descomentar set -ee executar novamente, não verá a lista de diretórios, pois o bash interrompe o processamento quando encontrar o erro lsd.


Essa resposta adiciona algum insight ou informação que ainda não foi fornecida por outras pessoas sobre a questão?
Charles Duffy

6
Eu acho que ele oferece uma explicação clara e sucinta da funcionalidade que não está presente nas outras respostas. Nada adicional, apenas mais focado do que as outras respostas.
Kallin Nagelberg

9

Esta é uma pergunta antiga, mas nenhuma das respostas aqui discute o uso de set -eaka set -o errexitnos scripts de manipulação de pacotes Debian. O uso desta opção é obrigatório nesses scripts, de acordo com a política Debian; aparentemente, a intenção é evitar qualquer possibilidade de uma condição de erro não tratado.

O que isso significa na prática é que você precisa entender sob quais condições os comandos executados podem retornar um erro e tratar explicitamente cada um desses erros.

Os truques comuns são, por exemplo, diff(retorna um erro quando há uma diferença) e grep(retorna um erro quando não há correspondência). Você pode evitar os erros com manipulação explícita:

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(Observe também como tomamos o cuidado de incluir o nome do script atual na mensagem e gravar mensagens de diagnóstico em erro padrão em vez de saída padrão.)

Se nenhum tratamento explícito for realmente necessário ou útil, não faça nada explicitamente:

diff this that || true
grep cat food || :

(O uso do :comando no-op do shell é um pouco obscuro, mas geralmente visto.)

Apenas para reiterar,

something || other

é uma abreviação de

if something; then
    : nothing
else
    other
fi

ou seja, dizemos explicitamente que otherdeve ser executado se e somente se somethingfalhar. A mão longa if(e outras instruções de controle de fluxo do shell como while, until) também é uma maneira válida de lidar com um erro (de fato, se não fosse, os scripts do shell set -enunca poderiam conter instruções de controle de fluxo!)

E também, apenas para ser explícito, na ausência de um manipulador como esse, set -efaria com que o script inteiro falhasse imediatamente com um erro se diffencontrasse uma diferença ou se grepnão encontrasse uma correspondência.

Por outro lado, alguns comandos não produzem um status de saída de erro quando você deseja. Os comandos geralmente problemáticos são find(o status de saída não reflete se os arquivos foram realmente encontrados) e sed(o status de saída não revela se o script recebeu alguma entrada ou se executou algum comando com êxito). Uma proteção simples em alguns cenários é canalizar para um comando que grita se não houver saída:

find things | grep .
sed -e 's/o/me/' stuff | grep ^

Deve-se observar que o status de saída de um pipeline é o status de saída do último comando nesse pipeline. Portanto, os comandos acima mascaram completamente o status de finde sed, e apenas informam se grepfinalmente foi bem-sucedido.

(É claro que o Bash tem set -o pipefail; mas os scripts de pacotes do Debian não podem usar os recursos do Bash. A política dita firmemente o uso do POSIX shpara esses scripts, embora esse nem sempre tenha sido o caso.)

Em muitas situações, isso é algo a ser observado separadamente ao codificar na defensiva. Às vezes, você precisa, por exemplo, passar por um arquivo temporário para ver se o comando que produziu essa saída foi concluído com êxito, mesmo quando o idioma e a conveniência o direcionariam a usar um pipeline de shell.


Esta é uma excelente resposta. e promove as melhores práticas. Eu tinha exatamente o mesmo problema do comando GREP e eu realmente não queria remover o 'set -e'
Minnie

7
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" shell will process and program exit, it will not proceed further
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.