Este script apenas tenta copiar o arquivo foo
para bar/
e, se não der certo, tenta novamente mais 2 vezes.
EDITAR: como se vê, o motivo pelo qual o segundo exemplo funciona e o primeiro não, é porque o uso de (...)
para executar um grupo de comandos após o &&
ou ||
branch é considerado um subshell e não afeta a variável externa.
Então, pergunta revisada: como posso agrupar alguns comandos como o primeiro exemplo, mas não usar um subshell para fazer isso?
EDIT 2: a resposta é substituir ( ... )
por { ...; }
!
#!/usr/bin/env bash
#
# Be safe
set -euo pipefail
# Init variables
__file="foo"
output_dir="bar"
logfile="test.log"
errorlevel=8
count=0
# Try three times to copy a file
until (( errorlevel == 0 || count == 3 )); do
cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile" \
&& (errorlevel=$?; echo "zero return, $errorlevel") \
|| (errorlevel=$?; echo "non-zero return, $errorlevel")
echo "errorlevel $errorlevel"
(( ++count ))
echo "count $count"
done
unset errorlevel
unset count
O problema:
Quando a cópia falha ou é bem-sucedida, a ramificação ||
ou &&
é capturada corretamente e define a variável errorlevel
a 1
ou 0
, respectivamente, conforme evidenciado com o echo $errorlevel
contido na ramo.
Mas o próximo comando, também um eco, retorna o valor inicial da variável 8
! Eu não entendo porque isso está acontecendo.
Algumas notas:
- Estou usando
&&
e ||
explicitamente porque tenho set -e
para tornar esse script o mais seguro possível (essa é, na verdade, uma pequena rotina em um script de 1500 linhas). Se eu não usar ||
para capturar um retorno diferente de zero, o script será encerrado. Alternativamente, eu poderia set +e
, fazer a rotina de cópia sem ter que usar &&
e ||
(capturando o errorlevel uma vez) e, em seguida, set -e
novamente, mas prefiro não porque não corresponde ao estilo do resto do meu script.
- Estou usando
set -o pipefail
para passar um nível de erro diferente de zero pelo pipeline, para que meu tee -a "$logfile"
nem sempre substitua minha saída errorlevel
.
- Estou usando
++count
em vez de count++
porque o primeiro retorna um nível de erro zero e o segundo retorna um nível de erro diferente de zero (e eu teria que fazer um(( count++ )) || true
feio por causa do set -e
explicado na nota # 1).
Eu tenho trabalhado em torno disso por agora, definindo a variável errorlevel
explicitamente para 1
ou 0
, já que eu realmente não me importo com o que é zeroerrorlevel
, mas eu ainda gostaria para saber por que o acima não está funcionando como eu esperava.
Veja a solução alternativa:
#!/usr/bin/env bash
#
set -euo pipefail
__file="foo"
output_dir="bar"
logfile="test.log"
errorlevel=1
count=0
until (( errorlevel == 0 || count == 5 )); do
cp -Lv --no-preserve=mode \
"$__file" \
"$output_dir/" \
| tee -a "$logfile" \
&& errorlevel=0 \
|| errorlevel=1
(( ++count ))
sleep 1
done
unset errorlevel
unset count
Qualquer ideia é muito apreciada!