resumo: Se bash
eu tentar atribuir a saída de uma função ausente a uma declare
variável previamente d (ou seja, não {constante, somente leitura}), posso detectar a falha com testes "normais". Mas se eu tentar atribuir a saída de uma função ausente a uma variável enquanto a estiver declare
digitando (por exemplo, para tornar a variável {constante, somente leitura}), não apenas a atribuição não falhará com testes "normais", como também não falha de força com builtins "normais". Como posso fazer o último caso falhar?
detalhes:
Recentemente, encontrei um problema em um bash
script muito maior, que tentei destilar nos 2 scripts a seguir. Basicamente, estou meio que fazendo TDD com bash
( snark > /dev/null
), então entre outras coisas
- Quero que os comandos/funções ausentes falhem rapidamente
- Eu quero evitar reescrever constantes
No entanto, bash
parece estar me permitindo atribuir a saída de uma função ausente a uma variável enquanto declare
var. Por exemplo, o seguinte script (salvo como /path/to/assign_at_declare.sh
) ...
#!/usr/bin/env bash
function foo() {
return 0
}
# function bar() {} # does not exist!
declare ret_val=''
declare -r MESSAGE_PREFIX="$(basename "${BASH_SOURCE}"):"
declare -r ERROR_PREFIX="${MESSAGE_PREFIX} ERROR:"
echo -e "\n${MESSAGE_PREFIX} a naïve 1st attempt:\n"
declare -ir FOO1_VAL="$(foo)" # this should succeed, and does
ret_val="${?}"
if [[ "${ret_val}" -ne 0 ]] ; then
>&2 echo "${ERROR_PREFIX} foo returned '${ret_val}', exiting ..."
exit 3
elif [[ -z "${FOO1_VAL}" ]] ; then
>&2 echo "${ERROR_PREFIX} foo returned null, exiting ..."
exit 4
else
echo "${MESSAGE_PREFIX} FOO1_VAL='${FOO1_VAL}'"
fi
declare -ir BAR1_VAL="$(bar)" # this should fail ... but doesn't
ret_val="${?}"
if [[ "${ret_val}" -ne 0 ]] ; then
>&2 echo "${ERROR_PREFIX} bar returned '${ret_val}', exiting ..."
exit 5
elif [[ -z "${BAR1_VAL}" ]] ; then
>&2 echo "${ERROR_PREFIX} bar returned null, exiting ..."
exit 6
else
echo "${MESSAGE_PREFIX} BAR1_VAL='${BAR1_VAL}'"
fi
echo -e "\n${MESSAGE_PREFIX} get tough using \`set\` builtins:\n"
# see https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
set -o errexit
set -o pipefail
declare -ir FOO2_VAL="$(foo)" # this should succeed, and does
ret_val="${?}"
if [[ "${ret_val}" -ne 0 ]] ; then
>&2 echo "${ERROR_PREFIX} foo returned '${ret_val}', exiting ..."
exit 3
elif [[ -z "${FOO2_VAL}" ]] ; then
>&2 echo "${ERROR_PREFIX} foo returned null, exiting ..."
exit 4
else
echo "${MESSAGE_PREFIX} FOO2_VAL='${FOO2_VAL}'"
fi
declare -ir BAR2_VAL="$(bar)" # this should fail ... but doesn't
ret_val="${?}"
if [[ "${ret_val}" -ne 0 ]] ; then
>&2 echo "${ERROR_PREFIX} bar returned '${ret_val}', exiting ..."
exit 5
elif [[ -z "${BAR2_VAL}" ]] ; then
>&2 echo "${ERROR_PREFIX} bar returned null, exiting ..."
exit 6
else
echo "${MESSAGE_PREFIX} BAR2_VAL='${BAR2_VAL}'"
fi
exit 0
... produz a seguinte saída:
assign_at_declare.sh: a naïve 1st attempt:
assign_at_declare.sh: FOO1_VAL='0'
/path/to/assign_at_declare.sh: line 27: bar: command not found
assign_at_declare.sh: BAR1_VAL='0'
assign_at_declare.sh: get tough using `set` builtins:
assign_at_declare.sh: FOO2_VAL='0'
/path/to/assign_at_declare.sh: line 56: bar: command not found
assign_at_declare.sh: BAR2_VAL='0'
Isso parece estranho, pois não observo esse comportamento se tento atribuir a saída da função ausente a uma variável após declare
ing a var (ou seja, se a var não for {constant, read-only}) como no script a seguir (salvo como /path/to/assign_after_declare.sh
) ...
#!/usr/bin/env bash
function foo() {
return 0
}
# function bar() {} # does not exist!
declare ret_val=''
declare -i foo_val=0
declare -i bar_val=0
declare -r MESSAGE_PREFIX="$(basename "${BASH_SOURCE}"):"
declare -r ERROR_PREFIX="${MESSAGE_PREFIX} ERROR:"
echo -e "\n${MESSAGE_PREFIX} following works as expected\n"
foo_val="$(foo)" # this should succeed, and does with/out `declare`
ret_val="${?}"
if [[ "${ret_val}" -ne 0 ]] ; then
>&2 echo "${ERROR_PREFIX} foo returned '${ret_val}', exiting ..."
exit 3
elif [[ -z "${foo_val}" ]] ; then
>&2 echo "${ERROR_PREFIX} foo returned null, exiting ..."
exit 4
else
echo "${MESSAGE_PREFIX} foo_val='${foo_val}'"
fi
bar_val="$(bar)" # this succeeds with `declare`, fails without
ret_val="${?}"
if [[ "${ret_val}" -ne 0 ]] ; then
>&2 echo "${ERROR_PREFIX} bar returned '${ret_val}', exiting ..."
exit 5
elif [[ -z "${bar_val}" ]] ; then
>&2 echo "${ERROR_PREFIX} bar returned null, exiting ..."
exit 6
else
echo "${MESSAGE_PREFIX} bar_val='${bar_val}'"
fi
exit 0
... que produz a seguinte saída:
assign_after_declare.sh: following works as expected
assign_after_declare.sh: foo_val='0'
/path/to/assign_after_declare.sh: line 29: bar: command not found
assign_after_declare.sh: ERROR: bar returned '127', exiting ...
Existe uma maneira de forçar bash
a falha rápida ao atribuir durante um declare
? Se sim, aguardo sua resposta.
Alternativamente, está bash
trabalhando como projetado? Em caso afirmativo, faça um link para uma referência; Tentei pesquisar esta pergunta, mas meus seletores estavam incorretos ou retornaram tantas respostas não relacionadas que foram inúteis.
declare
o status de retorno de é :Como nenhuma dessas situações se aplica, o Bash está funcionando como projetado, como você disse.
$(bar)
executabar
em um subshell, que sai com um erro e nenhuma saída padrão. A substituição resulta em uma string vazia, que é interpretada como zero para uma variável inteira.declare
então retorna 0, conforme documentado .Você pode detectar uma falha, por exemplo:
com fd 3 configurado adequadamente com antecedência. Tal configuração é deixada como exercício para o leitor.