Como posso fazer com que esse script saia com erro com base no resultado do loop for?

Como posso fazer com que esse script saia com erro com base no resultado do loop for?

Eu tenho um script bash que usaset -o errexit para que, em caso de erro, todo o script seja encerrado no ponto de falha.
O script executa um curlcomando que às vezes falha ao recuperar o arquivo pretendido - no entanto, quando isso ocorre, o script não sai com erro.

Eu adicionei umfor loop para

  1. pause por alguns segundos e tente novamentecurl comando
  2. use falsena parte inferior do loop for para definir um status de saída padrão 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 é que quando o curlcomando falha, o loop tenta novamente o comando cinco vezes - se todas as tentativas não forem bem-sucedidas, o loop for termina e o script principal é retomado - em vez de acionar o errexit.
Como posso fazer com que o script inteiro saia se esta curlinstrução falhar?

Responder1

Substituir:

done

com:

done || exit 1

Isso fará com que o código saia se ofor loop terminar com um código de saída diferente de zero.

A título de curiosidade, o 1in exit 1não é necessário. Um comando simples exitsairia com o status de saída do último comando executado, que seria false(código = 1) se o download falhasse. Se o download for bem-sucedido, o código de saída do loop será o código de saída do echocomando. echonormalmente sai com código = 0, sinalizando sucesso. Nesse caso, o|| não é acionado e o exitcomando não é executado.

Por último, observe que set -o errexitpode ser cheio de surpresas. Para uma discussão sobre seus prós e contras, consultePerguntas frequentes de Greg nº 105.

Documentação

De man bash:

para(( expr1 ; expr2 ; expr3 )) ;fazerlista ; feito
Primeiro, a expressão aritmética expr1 é avaliada de acordo com as regras descritas abaixo em AVALIAÇÃO ARITMÉTICA. A expressão aritmética expr2 é então avaliada repetidamente até ser avaliada como zero. Cada vez que expr2 é avaliado como um valor diferente de zero, list é executada e a expressão aritmética expr3 é avaliada. Se alguma expressão for omitida, ela se comportará como se fosse avaliada como 1. O valor de retorno é o status de saída do último comando da lista executado ou falso se alguma das expressões for inválida.[Enfase adicionada]

Responder2

Se preferir parar o loop na primeira falha, e também evitar set -e, você pode fazer algo assim:

for i in `seq 1 10`; do
  run_command || exit 1;
done

Responder3

Se você tiver errexitdefinido, a falseinstrução deverá fazer com que o script seja encerrado imediatamente. A mesma coisa se o curlcomando falhar.

Seu script de exemplo, conforme escrito, deve sair após a curlfalha do primeiro comando na primeira vez que ele chamar falsese errexit estiver definido.

Para ver como funciona (eu uso a abreviação -ede to set errexit:

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

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

Portanto, se o curlcomando for executado mais de uma vez, este script não está errexitdefinido.

Responder4

set -o errexitpode ser complicado em loops e subshells, porque você precisa retornar do processo.

Quebrar um loop (mesmo em operação normal) é considerado uma má prática. Você pode me chamar de antiquado por preferir um loop while em vez de um loop for 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

informação relacionada