Como posso obter esse script para a saída de erro com base no resultado de loop?

9

Eu tenho um script bash que usa set -o errexit para que, no erro, todo o script saia no ponto de falha.
O script executa um comando curl , que às vezes falha ao recuperar o arquivo pretendido. No entanto, quando isso ocorre, o script não soluciona erros.

Eu adicionei um loop for a

  1. pause por alguns segundos e tente novamente o comando curl
  2. use false na parte inferior do loop for para definir um status de saída diferente de zero - se o comando curl for bem-sucedido, o loop será interrompido e o status de saída do último comando deverá ser zero.
#! /bin/bash

set -o errexit

# ...

for (( i=1; i<5; i++ ))
do
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    if [ -f ~/.vim/autoload/pathogen.vim ]
    then
        echo "file has been retrieved by curl, so breaking now..."
        break;
    fi

    echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
    sleep 5
    # exit with non-zero status so main script will errexit
    false

done

# rest of script .....

O problema é quando o comando curl falha, o loop tenta novamente o comando cinco vezes - se todas as tentativas não forem bem-sucedidas, o loop for é concluído e o script principal é retomado, em vez de acionar errexit .
Como posso obter o script inteiro para sair se essa instrução curl falhar?

    
por the_velour_fog 06.11.2015 / 08:10

5 respostas

10

Substituir:

done

com:

done || exit 1

Isso fará com que o código seja encerrado se o loop for sair com um código de saída diferente de zero.

Como ponto de trivialidade, o 1 in exit 1 não é necessário. Um comando exit simples sairia com o status de saída do último comando executado, que seria false (code = 1) se o download falhar. Se o download for bem-sucedido, o código de saída do loop será o código de saída do comando echo . echo normalmente sai com código = 0, com sinal de sucesso. Nesse caso, o || não é acionado e o comando exit não é executado.

Por fim, observe que set -o errexit pode ser cheio de surpresas. Para uma discussão de seus prós e contras, consulte Perguntas frequentes de Greg # 105 .

Documentação

De man bash :

for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression expr1 is evaluated according the rules described below under ARITHMETIC EVALUATION. The arithmetic expression expr2 is then evaluated repeatedly until it evaluates to zero. Each time expr2 evaluates to a non-zero value, list is executed and the arithmetic expression expr3 is evaluated. If any expression is omitted, it behaves as if it evaluates to 1. The return value is the exit status of the last command in list that is executed, or false if any of the expressions is invalid. [Emphasis added]

    
por 06.11.2015 / 08:27
1

Se você tiver errexit set, a instrução false fará com que o script saia imediatamente. Mesma coisa se o comando curl falhou.

Seu script de exemplo, como escrito, deve sair após a primeira falha do comando curl na primeira vez em que chamar false se o errexit estiver definido.

Para ver como funciona (uso a abreviação -e para definir errexit :

$ ( set -e;  false; echo still here )
$

$ ( set +e;  false; echo still here )
still here
$

Portanto, se o comando curl for executado mais de uma vez, esse script não terá errexit set.

    
por 06.11.2015 / 08:51
1

set -o errexit pode ser complicado em loops e subshells, porque você precisa repassar o caminho para fora do processo.

Quebrar um loop (mesmo em operação normal) é considerado uma prática ruim. Você pode me chamar de old-school para preferir um while-loop em vez de um for-loop para duas condições, mas acho melhor ler:

i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
    [ $i -eq 1 ] || sleep 5
    echo "attempt number: "$i
    curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    RET=$?
    i=$((i+1))
done
exit $RET
    
por 06.11.2015 / 12:42
0

Se errexit for definido e o comando curl falhar, o script será finalizado logo após o comando curl com falha. No manual bash, não há indício de que set -e ignore qualquer status de retorno com falha de um único em um comando composto. Isso seria apenas o caso se o comando composto fosse executado em um contexto no qual set -e fosse ignorado.

Tente um exemplo ligeiramente adaptado postado por RobertL. Isso pára na primeira iteração logo após o falso comando:

( set -e; for (( i=1; i<5; i++ )); do echo $i; false; echo "${i}. iteration done"; done ; echo "loop done" )
    
por 07.11.2015 / 14:24
0

Você pode simplesmente adicionar a opção --fail ao comando curl, isso resolverá seu problema, o script falhará e sairá no erro se o comando curl falhar, se for muito útil também ao usar o curl no pipeline jenkins:

curl -LSso --fail ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
    
por 05.04.2018 / 09:24