Atribuições são como comandos com um status de saída, exceto quando há substituição de comando?

10

Veja os seguintes exemplos e suas saídas em shells POSIX:

  1. false;echo $? ou false || echo 1 : 1
  2. false;foo="bar";echo $? ou foo="bar" && echo 0 : 0
  3. foo=$(false);echo $? ou foo=$(false) || echo 1 : 1
  4. foo=$(true);echo $? ou foo=$(true) && echo 0 : 0

Como mencionado pela resposta mais votada no link :

$? is used to find the return value of the last executed command.

Isto é provavelmente um pouco enganador neste caso, então vamos pegar a definição POSIX que também é citada em um post daquele thread:

? Expands to the decimal exit status of the most recent pipeline (see Pipelines).

Portanto, parece que uma atribuição em si conta como um comando (ou melhor, uma parte de pipeline) com um valor de saída zero, mas que se aplica antes do lado direito da atribuição (por exemplo, as chamadas de substituição de comando nos meus exemplos aqui).

Eu vejo como esse comportamento faz sentido do ponto de vista prático, mas parece um pouco incomum para mim que a atribuição em si contaria nessa ordem. Talvez para deixar mais claro por que é estranho para mim, vamos supor que a atribuição fosse uma função:

ASSIGNMENT( VARIABLE, VALUE )

então foo="bar" seria

ASSIGNMENT( "foo", "bar" )

e foo=$(false) seriam algo como

ASSIGNMENT( "foo", EXECUTE( "false" ) )

o que significa que EXECUTE é executado primeiro e somente depois ASSIGNMENT é executado, mas ainda é o status EXECUTE que importa aqui.

Estou correto em minha avaliação ou estou entendendo mal / faltando alguma coisa? Essas são as razões certas para eu ver esse comportamento como "estranho"?

    
por phk 29.01.2017 / 17:12

2 respostas

10

O status de saída para atribuições é estranho . A maneira mais óbvia de uma tarefa falhar é se a variável de destino estiver marcada com readonly .

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Observe que nem os caminhos verdadeiros nem falsos da instrução if foram considerados, mas a falha na atribuição interrompeu a execução de toda a instrução. bash no modo POSIX e ksh93 e zsh todos abortarão um script se uma atribuição falhar.

Para citar o padrão POSIX neste :

A command without a command name, but one that includes a command substitution, has an exit status of the last command substitution that the shell performed.

Esta é exatamente a parte da gramática de shell envolvida em

 foo=$(err 42)

que vem de um simple_command (simple_command → cmd_prefix → ASSIGNMENT_WORD). Portanto, se uma atribuição for bem-sucedida, o status de saída será zero, a menos que a substituição de comando esteja envolvida; nesse caso, o status de saída será o status do último. Se a atribuição falhar, o status de saída será diferente de zero, mas talvez não seja possível capturá-lo.

    
por 29.01.2017 / 19:18
4

Você diz:

… it appears as if an assignment itself counts as a command … with a zero exit value, but which applies before the right side of the assignment (e.g., a command substitution call…)

Isso não é uma maneira terrível de ver isso. Mas é uma ligeira simplificação. O status geral de retorno de

A=$(cmd1)  B=$(cmd2)  C=$(cmd3)  D=$(cmd4)  E=mc2
é o status de saída de cmd4 . A designação E= que ocorre após a atribuição D= não define o status geral de saída como 0.

Além disso, como icarus aponta , as variáveis podem ser definidas como somente leitura. Considere a seguinte variação no exemplo da icarus:

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

Mesmo que A seja somente leitura, bash executa a substituição de comando à direita de A= - e então anula o comando porque A é somente leitura. Isso contradiz ainda mais sua interpretação que o valor de saída da atribuição se aplica antes do lado direito da tarefa.

    
por 31.01.2017 / 07:13