Na verdade, é mais o seguinte:
$ 'nonsense'
bash: nonsense: command not found
$ echo "$?"
127
Isso é surpreendente aqui.
Pedimos que bash execute o comando que resulta do operador split + glob no stdout de nonsense . Como nonsense não produz saída, ele não executa nenhum comando, portanto você pode achar que o status de saída deve ser 0.
Mas, na verdade, quando uma linha de comando simples não possui argumento, apenas atribuição ou redirecionamento, o status de saída é o da última substituição de comando em atribuição e palavras normais (não em alvos de redirecionamento) executadas (embora a falha nos redirecionamentos seja também afeta o status de saída).
Isso é especialmente útil com atribuições.
Em:
output=$(grep pattern file)
status=$?
Você pode obter o status de saída e saída de grep , o que não seria possível se $? fosse o status de saída desse não-comando.
Em:
output=$(cmd1) cmd2
É aí que existem palavras de atribuição e palavras de argumento, o status de saída de cmd1 é ignorado. $? conterá o status de saída de cmd2 .
E também $output será definido apenas para cmd2 . Exceção para isso é quando cmd2 é um especial embutido.
eval é um especial .
$ a=0; a=1 eval; echo "$a"
1
Em bash e nas mais modernas caixas POSIX.
a='exit 5' eval; echo "$?"
ou
eval 'exit 5'; echo "$?"
A saída seria 0, pois é o resultado da execução de eval sem argumento. Mas esse não era o caso no shell Bourne ou no ksh88, onde para construtores especiais você obteria o status de saída de exit 5 .
Nestas conchas, você também encontrará:
$ a='exit 3' set x; echo "$?"
3
Como set é outro especial incorporado.
. é outro especial incorporado. No shell Bourne e no ksh88:
$ . /some/file 'exit 4'; echo "$?"
4
(contanto que /some/file não execute nenhum comando)