Para capturar stdout e o código de saída quando o -e
sinalizador (sair do shell imediatamente quando um comando falha) não estiver definido, eu usaria
OUTPUT="$(my_command)"
exit_code=$?
Para capturar o código de saída com -e
set, eu usaria
exit_code=0
my_command || exit_code=$?
Mas combinar as duas abordagens não funciona:
exit_code=0
OUTPUT="$(my_command || exit_code=$?)"
Isso não é definido exit_code
porque é atribuído em um subshell e a cópia no shell pai não é atualizada.
Então, como eu capturo stdout e o código de saída? Eu poderia unset -e
ou, por exemplo, escrever o código de saída em um tempfile, mas eu gostaria de algo mais simples.
Devo ter tido um lapso de memória quando estava tentando resolver esse problema. A solução é direta. No entanto, não consegui encontrar uma resposta com meu mecanismo de busca, então decidi adicionar essa pergunta aqui.
Solução:
ou seja, mover para
|| exit_code=$?
fora da subcamada.NB: não prefixe isto com
export
oulocal
, fazendo isso irá sobrescrever o código de saída demy_command
. Definir variáveis comolocal
ouexport
pode ser feito em em um comando separado.Isso se resume a como desabilitar o efeito de
errexit
um comando , aqui uma atribuição de variável.errexit
é cancelado quando um comando está envolvido em uma condição como emif cmd; then...
(o mesmo comwhile
/until
; o mesmo emif cmd; other-cmd; then...
)! cmd
cmd || ...
cmd && ...
Em,
output=$(cmd)
você tem dois comandos:cmd
:Com
errexit
on, secmd
falhar, o subshell sai (com o mesmo status de saída), você descobrirá, por exemplo, que emvar=$(false; echo here>&2)
,echo here
não é executado; o status de saída da atribuição da variável será o daquele subshell, portanto, também uma falha, então o processo do shell que interpreta essa atribuição também sairá.Se essa atribuição for parte de uma condição , ela
errexit
será desabilitada recursivamente em todo o código shell que pode ser interpretado ali.Em:
errexit
é desabilitado tanto para a atribuição de variáveis quanto paracmd
, e secmd
for uma função, por exemplo, para cada comando que essa função executa, mesmo em subshells, mesmo que essa função chame explicitamenteset -o errexit
/set -e
.Aqui, se você quiser lidar com a falha,
cmd
independentemente deerrexit
estar habilitada ou não, você pode usar oif ! output=$(cmd); then
acima ou:Se você precisar obter o valor real do status de saída no manipulador, você pode fazer:
Se você quiser apenas registrar o status de saída por enquanto, talvez para lidar com isso mais tarde, a maneira canônica de cancelar apenas
errexit
um comando (com a ressalva, conforme observado acima, de que ele se aplica recursivamente) é anexar&& true
.Significa que você quer ignorar completamente qualquer falha de
cmd
. Não apenaserrexit
é cancelado, mas o status geral de saída refletirá o sucesso de . Enquanto:Cancela
errexit
, mas também preservacmd
o status de saída de no status de saída de toda a lista and-or. Então:Se o código for
output=$(cmd1; cmd2)
, você provavelmente desejará alterá-looutput=$(cmd1 && cmd2)
porque, comerrexit
o cancelamento,cmd2
ele será executado mesmo secmd1
falhar.Ou você pode desistir
errexit
completamente , como muitas pessoas fazem e recomendam fazer , e fazer o tratamento de erros adequado em todo o seu script. É um bom exercício parar e pensar no que deveria acontecer se um comando falhasse e decidir o que fazer nesse caso, em vez de esperarerrexit
que faça a coisa certa, o que na prática raramente acontece consistentemente, exceto nos scripts mais simples.Uma maneira possível é usar um comando como este:
Com isso você evita uma fila e atribui diretamente o código de saída
No caso de
-e
set, você pode desabilitá-lo temporariamente, executar o comando e então setá-lo novamente. Aqui você pode encontrar um script de exemplo para fazer isso.