TL; DR: No caso de export FOO=bar
, o bash invoca sua criação de ambiente temporário, define FOO=bar
nesse ambiente e, em seguida, gera um comando final de export FOO
. Nesse ponto, FOO
é considerado o último argumento.
Ah, o muito abusado $_
:
($_, an underscore.) At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file.
Vamos ver algumas variações:
$ man; echo $_
What manual page do you want?
man
$ man foo; echo $_
No manual entry for foo
foo
$ echo; echo $_
echo
$ echo bar foo; echo $_
bar foo
foo
$ foo=x eval 'echo $foo'; echo $_
x
echo $foo
$ bar() { man $1; }; echo $_
foo
$ for (( i=0; $i<0; i=i+1 )); do echo $i; done; echo $_
foo
$ bar; echo $_
What manual page do you want?
man
$ bar foo; echo $_
No manual entry for foo
foo
$ MANPATH=/tmp; echo $_
$ export MANPATH=/tmp; echo $_
MANPATH
Então, vemos três padrões aqui:
- Comandos invocados a partir do sistema de arquivos, funções e built-ins se comportam como geralmente esperado:
$_
é configurado para o nome do comando se nenhum argumento, caso contrário, o último dos argumentos apresentados.
- Após definições de funções, loops e outras construções lógicas:
$_
não é modificado.
- Todo o resto:
$_
está definido para algo não esperado; estranho.
Eu já instrumentei o código para fornecer algumas dicas sobre a estranheza.
$ ./bash --noprofile --norc -c 'man foo'
lastword=[man]
lastarg=[foo]
$ ./bash --noprofile --norc -c 'export FOO=bar'
lastword=[export]
lastarg=[FOO=bar]
bind_variable, name=[FOO], value=[bar]
before bind_lastarg, lastarg=[FOO]
bind_lastarg, arg=[FOO]
bind_variable, name=[_], value=[FOO]
$ ./bash --noprofile --norc -c 'declare FOO=bar'
lastword=[declare]
lastarg=[FOO=bar]
bind_variable, name=[FOO], value=[(null)]
before bind_lastarg, lastarg=[FOO=bar]
bind_lastarg, arg=[FOO=bar]
bind_variable, name=[_], value=[FOO=bar]
Você pode ver que o analisador vê o último argumento esperado ( lastarg=
) em todos os casos, mas o que acontece depois disso depende do que o bash acha que deve acontecer. Veja execute_cmd.c, execute_simple_command () .
No caso de export FOO=bar
, bash faz a atribuição e, em seguida, exporta a variável. Isso parece consistente com a afirmação da documentação de que o último argumento foi calculado após a expansão.