Está documentado (para POSIX) na Seção 2.9.1 Comandos simples
das especificações básicas do Open Group. Há uma parede de texto lá; Dirijo sua atenção ao último parágrafo:
Se houver um nome de comando, a execução continuará como descrito em Pesquisa e Execução de Comando . Se não houver um nome de comando, mas o comando contiver uma substituição de comando, o comando será concluído com o status de saída da última substituição de comando executada. Caso contrário, o comando será concluído com um status de saída zero.
Então, por exemplo,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
É assim que o bash também funciona. Mas veja também a seção “não tão simples” no final.
phk , em sua pergunta Atribuições são como comandos com status de saída, exceto quando há substituição de comando? , sugere
… Parece que uma atribuição em si conta como um comando… com um valor de saída zero, mas que se aplica antes do lado direito da atribuição (por exemplo, uma chamada de substituição de comando…)
Essa não é uma maneira terrível de encarar. Um esquema em bruto para a determinação do estado de retorno de um comando simples (uma que não contém ;
, &
, |
, &&
ou ||
) é:
- Digitalize a linha da esquerda para a direita até chegar ao final ou a uma palavra de comando (geralmente o nome de um programa).
- Se você vir uma atribuição de variável, o status de retorno da linha poderá ser 0.
- Se você vir uma substituição de comando - ou seja,
$(…)
- obtenha o status de saída desse comando.
- Se você alcançar um comando real (não em uma substituição de comando), obtenha o status de saída desse comando.
O status de retorno da linha é o último número que você encontrou.
Substituições de comando como argumentos para o comando, por exemplo, foo $(bar)
não contam; você obtém o status de saída foo
. Parafraseando a notação do phk , o comportamento aqui é
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Mas isso é uma pequena simplificação excessiva. O status geral de retorno de
A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
é o status de saída de . A atribuição que ocorre após a atribuição não define o status geral da saída como 0.
cmd4
E=
D=
icarus , em sua resposta à pergunta do phk , levanta um ponto importante: as variáveis podem ser definidas como somente leitura. O terceiro ao último parágrafo da Seção 2.9.1 da norma POSIX diz:
Se alguma das atribuições de variáveis tentar atribuir um valor a uma variável para a qual o atributo readonly está definido no ambiente shell atual (independentemente de a atribuição ser feita nesse ambiente), ocorrerá um erro de atribuição de variável. Consulte Consequências dos erros do shell para obter as consequências desses erros.
então se você diz
readonly A
C=Garfield A=Felix T=Tigger
o status de retorno é 1. Não importa se as cordas Garfield
, Felix
e / ou Tigger
são substituídos com substituição de comando (s) - mas veja notas abaixo.
A Seção 2.8.1 Consequências dos erros do shell possui outro monte de texto e uma tabela e termina com
Em todos os casos mostrados na tabela em que é necessário que um shell interativo não saia, o shell não deve executar nenhum processamento adicional do comando no qual o erro ocorreu.
Alguns dos detalhes fazem sentido; alguns não:
- A
A=
atribuição às vezes aborta a linha de comando, como a última frase parece especificar. No exemplo acima, C
está definido como Garfield
, mas T
não está definido (e, é claro, também não A
).
- Da mesma forma,
executa ,
mas não . Mas, nas minhas versões do bash (que incluem 4.1.X e 4.3.X), ele é executado . (Aliás, isso impacta ainda mais a interpretação do phk de que o valor de saída da atribuição se aplica antes do lado direito da atribuição.)
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Mas aqui está uma surpresa:
Nas minhas versões do bash,
somente leitura A
C = algo A = algo T = algo cmd 0
não executar. Em particular,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
executa
e , mas não . (Observe que esse é o oposto de seu comportamento quando não há comando.) E ele se estabelece (e também ) no ambiente de . Gostaria de saber se isso é um bug no bash.
cmd1
cmd3
cmd2
T
C
cmd0
Não tão simples:
O primeiro parágrafo desta resposta se refere a "comandos simples".
A especificação diz,
Um "comando simples" é uma sequência de atribuições e redirecionamentos de variáveis opcionais, em qualquer sequência, opcionalmente seguidos por palavras e redirecionamentos, finalizados por um operador de controle.
Estas são instruções como as do meu primeiro bloco de exemplo:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
os três primeiros incluem atribuições de variáveis e os três últimos incluem substituições de comandos.
Mas algumas atribuições de variáveis não são tão simples.
o bash (1) diz:
Instruções de atribuição também pode aparecer como argumentos para o alias
, declare
, typeset
, export
, readonly
, e local
builtin comandos ( declaração de comandos).
Para export
, a especificação POSIX diz,
ESTADO DE SAÍDA
0 0Todos os operandos de nomes foram exportados com sucesso.
> 0Pelo menos um nome não pôde ser exportado ou a -p
opção foi especificada e ocorreu um erro.
E o POSIX não suporta local
, mas o bash (1) diz:
É um erro usar local
quando não estiver em uma função. O status de retorno é 0, a menos que local
seja usado fora de uma função, um nome inválido seja fornecido ou nome seja uma variável somente leitura.
Lendo nas entrelinhas, podemos ver que comandos de declaração como
export FOO=$(bar)
e
local FOO=$(bar)
são mais como
foo $(bar)
na medida em que ignorar o status de saída de bar
e dar-lhe um status de saída baseado no comando principal ( export
, local
ou foo
). Então, nós temos esquisitices como
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
que podemos demonstrar com
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
e
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
Felizmente, o ShellCheck pega o erro e gera SC2155 , que aconselha que
export foo="$(mycmd)"
deve ser alterado para
foo=$(mycmd)
export foo
e
local foo="$(mycmd)"
deve ser alterado para
local foo
foo=$(mycmd)
local
isso se relaciona? Por exemplolocal foo=$(bar)
?