Erro de ignorância do Bash para um comando específico


444

Estou usando as seguintes opções

set -o pipefail
set -e

No script bash para interromper a execução por erro. Eu tenho ~ 100 linhas de script em execução e não quero verificar o código de retorno de todas as linhas do script.

Mas para um comando em particular, quero ignorar o erro. Como eu posso fazer isso?

Respostas:


757

A solução:

particular_script || true

Exemplo:

$ cat /tmp/1.sh
particular_script()
{
    false
}

set -e

echo one
particular_script || true
echo two
particular_script
echo three

$ bash /tmp/1.sh
one
two

three nunca será impresso.

Além disso, quero acrescentar que, quando pipefailativado, é suficiente para o shell pensar que todo o pipe possui código de saída diferente de zero quando um dos comandos no pipe possui código de saída diferente de zero (com pipefailoff deve ser o último) .

$ set -o pipefail
$ false | true ; echo $?
1
$ set +o pipefail
$ false | true ; echo $?
0

15
+1. Como o Manual de Referência do Bash explica : "O shell não sai" quando o -eatributo está definido "se o comando que falhar fizer parte da lista de comandos imediatamente após uma palavra while- untilchave ou , parte do teste em uma ifinstrução, parte de qualquer comando executado em uma lista &&ou, ||exceto o comando após o comando final &&ou ||, qualquer comando em um pipeline, exceto o último, ou se o status de retorno do comando estiver sendo invertido !. "
Ruakh 27/06

@IgorChubin Não sei porquê, mas isso não faz produção de trabalho = (ldd $2/bin/* || true) | grep "not found" | wc -lroteiro está sendo finalizado após esta linha quando a falha ldd retorno
Vivek Goel

1
(ldd $2/bin/* || true) | grep "not found" | wc -l || true
Igor Chubin

1
por causa de set -o pipefail. quando grepnão encontra nada, ele retorna um código de saída diferente de zero e é suficiente para o shell pensar que todo o canal possui um código de saída diferente de zero.
Igor Chubin

3
Se você deseja a opção de preservar o valor de retorno (sem sair), tente mycommand && true. Isso permite que você verifique o código de retorno nas etapas subseqüentes e manipule-o programaticamente.
Ed Ost

178

Basta adicionar || trueapós o comando em que deseja ignorar o erro.


11
Eu acho que é importante adicionar isso: esse método permitirá que o código de resposta e a mensagem de erro persistam, enquanto o "!" O método descrito abaixo alterará o código de resposta e, portanto, não gerará o erro. Isso é importante ao usar set -ee tentar capturar uma mensagem de erro: por exemploset -e; TEST=$(foo 2>&1 || true); echo $TEST
Spanky

87

Não pare e também salve o status de saída

Caso você queira que seu script não pare, se um comando em particular falhar e também salvar o código de erro do comando com falha:

set -e
EXIT_CODE=0
command || EXIT_CODE=$?
echo $EXIT_CODE

2
Este é exatamente o que eu precisava
sarink

não funciona para mim por alguma razão ... oh bem
Jeef

EXIT_CODE não está definido como zero quando o comando retorna 0. Porém, códigos de saída diferentes de zero são capturados. Alguma idéia do porquê?
Ankita13

@ Ankita13 Porque se fosse zero, o primeiro commandfoi bem-sucedido e é necessário executar o que está depois ||. leia como: se commandfalhar, faça EXIT_CODE=$?. Você provavelmente poderia apenas fazer command || echo "$?"ou usar "trap" para uma depuração mais detalhada. Veja isso -> stackoverflow.com/a/6110446/10737630
tinnick 23/03

63

Mais concisamente:

! particular_script

A partir da especificação POSIX referente a set -e(ênfase minha):

Quando essa opção está ativada, se um comando simples falhar por algum dos motivos listados em Consequências de erros do shell ou retornar um valor de status de saída> 0 e não fizer parte da lista composta após algum tempo, até ou se a palavra-chave e não faz parte de uma lista AND ou OR e não é um pipeline precedido pelo! palavra reservada , a concha deve sair imediatamente.


16
Isso apenas inverte o código de saída de um comando, portanto, o comando concluído com êxito retornará 1 em vez de 0 e falhará no script -e.
Marboni

17
Meu entendimento é que !isso impedirá que o shell saia, não importa o quê. Este script exibe a mensagem Still alive!quando eu o executo, indicando que o script foi executado até a conclusão. Você está vendo um comportamento diferente?
Lily Finley

7
você está certo, inverte o status de saída, mas não falha o script quando o comando termina com 0 ou 1. Fiquei desatento. De qualquer forma, obrigado por citar a documentação, coloquei minha expressão em iforação e resolvi o problema.
Marboni

42

Em vez de "retornar true", você também pode usar o utilitário "noop" ou nulo (conforme referido nas especificações do POSIX ) :e apenas "não fazer nada". Você salvará algumas letras. :)

#!/usr/bin/env bash
set -e
man nonexistentghing || :
echo "It's ok.."

3
embora ||: não seja tão claro quanto || verdade (que pode confundir os usuários não especialistas), eu gosto concisão
David

Essa variante não retorna nenhum texto que possa ser importante no IC.
Kivagant 17/05/19

5

Se você deseja impedir a falha do seu script e coletar o código de retorno:

command () {
    return 1  # or 0 for success
}

set -e

command && returncode=$? || returncode=$?
echo $returncode

returncode é coletado, independentemente de o comando ter êxito ou falhar.


2

Uso o snippet abaixo ao trabalhar com ferramentas CLI e quero saber se algum recurso existe ou não, mas não me importo com a saída.

if [ -z "$(cat no_exist 2>&1 >/dev/null)" ]; then
    echo "none exist actually exist!"
fi

1
Esta parece ser uma maneira realmente indireta e moderadamente cara de dizer #if [ -r not_exist ]
tripleee

1

Eu meio que gosto desta solução:

: `particular_script`

O comando / script entre os ticks anteriores é executado e sua saída é alimentada ao comando ":" (que é o equivalente a "true")

$ false
$ echo $?
1
$ : `false`
$ echo $?
0

editar: Corrigido erro de digitação feio


0

enquanto || trueé o preferido, mas você também pode fazer

var=$(echo $(exit 1)) # it shouldn't fail

0

Nenhuma solução funcionou para mim a partir daqui, então encontrei outra:

set +e
find "./csharp/Platform.$REPOSITORY_NAME/obj" -type f -iname "*.cs" -delete
find "./csharp/Platform.$REPOSITORY_NAME.Tests/obj" -type f -iname "*.cs" -delete
set -e

Isso é útil para CI e CD. Dessa forma, as mensagens de erro são impressas, mas o script inteiro continua sendo executado.


0
output=$(*command* 2>&1) && exit_status=$? || exit_status=$?
echo $output
echo $exit_status

Exemplo de uso para criar um arquivo de log

log_event(){
timestamp=$(date '+%D %T') #mm/dd/yy HH:MM:SS
echo -e "($timestamp) $event" >> "$log_file"
}

output=$(*command* 2>&1) && exit_status=$? || exit_status=$?

if [ "$exit_status" = 0 ]
    then
        event="$output"
        log_event
    else
        event="ERROR $output"
        log_event
fi

0

Obrigado pela solução simples aqui de cima:

<particular_script/command> || true

A construção a seguir pode ser usada para ações adicionais / solução de problemas de etapas de script e opções adicionais de controle de fluxo:

if <particular_script/command>
then
   echo "<particular_script/command> is fine!"
else
   echo "<particular_script/command> failed!"
   #exit 1
fi

Podemos travar as ações adicionais e, exit 1se necessário.

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.