Qual é o último argumento do comando anterior?

12

$_ é dito ser o último argumento do comando anterior.

Então, eu me pergunto por que não é, EDITOR="emacs -nw"mas EDITORno exemplo a seguir?

Por que não faz "emacs -nw"parte do último argumento?

De maneira mais geral, quais são as definições de um argumento e o último argumento?

Obrigado.

$ export EDITOR="emacs -nw"
$ echo $_
EDITOR
Tim
fonte
3
Eu acho que é pela mesma razão que a verificação de shell diz para você não exportar variáveis ​​na mesma linha que você as atribui. A atribuição acontece e a variável é exportada. EDITORé um argumento para exportar
jesse_b 12/04
FWIW pdkshe dashincluirá o valor que foi atribuído, mas ksh93se comportará da mesma bashforma.
Kusalananda
zsh:, export FOO=bar; echo $_imprime export.
Ilkkachu
@ Jessesse_b A coisa toda é o último argumento / operando (incluindo o valor atribuído), mas pode ter algo a ver com o fato de exportser um utilitário interno.
Kusalananda
ksh: typeset -x FOO=bardepois echo $_imprime FOO, mas no Bash declare -x FOO=bar; echo $_imprime FOO=bar.
Ilkkachu # 12/18

Respostas:

13

Bash processa atribuições de variáveis, quando eles estão autorizados como argumentos (com alias, declare, export, local, readonly, e typeset), antes de mais nada (ou melhor, identifica-los antes de qualquer outra coisa - expansão se aplica aos valores atribuídos às variáveis). Quando se trata de expansão de palavras, o comando restante é export EDITOR, então _é definido como EDITOR.

De um modo geral, argumentos são as “palavras” restantes após a expansão (que não inclui atribuições e redirecionamentos de variáveis).

Consulte Expansão de comandos simples no manual do Bash para obter detalhes.

Stephen Kitt
fonte
E eu percebo declareo comportamento não coincide com o que estou descrevendo ...
Stephen Kitt
Bem, não é muito consistente nisso. declare a=b; echo $_impressões a=b; export c=d; echo $_imprime apenas c. aliasparece imprimir apenas o nome, localpor outro lado, imprime todo o argumento. E readonlytambém imprime apenas o nome, o que achei um pouco surpreendente, já que eu pensaria readonlye localseria semelhante declare.
Ilkkachu
1
@ilkkachu heh, percebi isso também (veja acima). exporte readonlysão declarados em conjunto setattr.def, declare, local, e typesetsão declarados no declare.def, aliasestá sozinho no alias.def.
Stephen Kitt
Obrigado. Quando atribuições de variáveis ​​são usadas como argumentos para alguns comandos, (1) "(ou melhor, identifica-as antes de qualquer outra coisa - a expansão se aplica aos valores atribuídos às variáveis)", você quer dizer que a expansão acontece com os valores antes de executar a atribuição de variáveis? (2) "Quando se trata de expansão de palavras, o comando restante é EDITOR de exportação", você quer dizer que a atribuição de variável ocorre antes da expansão? As duas citações parecem se contradizer.
Tim
Obrigado. Eu estou um pouco confuso. Quando atribuições de variáveis ​​são usadas como argumentos para alias, declare, export, local, readonlye typeset. O que acontece primeiro e depois? "Quando se trata de expansão de palavras, o comando restante é export EDITOR", você implica que a atribuição de variável EDITOR="emacs -nw"ocorre antes da expansão? Caso contrário, por que o comando restante não contém a atribuição como argumento? Se sim, a expansão dos valores atribuídos às variáveis ​​não precisa ocorrer antes de executar a atribuição das variáveis?
Tim
4

TL; DR: No caso de export FOO=bar, o bash chama sua criação temporária de ambiente, define FOO=barnesse ambiente e gera um comando final de export FOO. Nesse ponto, FOOé considerado o último argumento.


Ah, os muito abusados $_:

($ _, um sublinhado.) Na inicialização do shell, defina o nome do caminho absoluto usado para chamar o shell ou script de shell sendo executado conforme passado na lista de ambiente ou argumento. Posteriormente, expande para o último argumento para o comando anterior, após a expansão. Defina também o nome do caminho completo usado para chamar cada comando executado e colocado no ambiente exportado para esse comando. Ao verificar o correio, este parâmetro mantém o nome do arquivo de correio.

Vejamos 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:

  • Os comandos invocados no sistema de arquivos, funções e built-ins se comportam como geralmente esperado: $_é definido como o próprio nome do comando se não houver argumentos, caso contrário, o último dos argumentos apresentados.
  • Após definições de função, loops e outras construções lógicas: $_não é modificado.
  • Tudo o resto: $_está definido como algo não muito esperado; esquisito.

Eu 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 depende do que o bash pensa que deve acontecer. Consulte execute_cmd.c, execute_simple_command () .

No caso de export FOO=bar, o bash faz a atribuição e depois 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.

bispo
fonte
1
Como o shell sabe que você está verificando e-mails?
precisa saber é o seguinte
@rackandboneman sem confirmar, eu suspeito que as verificações internas feitas com base emMAILCHECK
Jeff Schaller
2

Para responder à pergunta do título, tente !$:

$ export EDITOR="emacs -nw"
$ echo !$
EDITOR=emacs -nw

Isso é expansão da história. Na página de manual do bash:

A expansão do histórico é realizada imediatamente após a leitura de uma linha completa, antes que o shell a divida em palavras. Ocorre em duas partes. A primeira é determinar qual linha da lista de histórico usar durante a substituição. O segundo é selecionar partes dessa linha para inclusão na atual. A linha selecionada da história é o evento, e as partes dessa linha em que são feitas as ações são palavras.

...

Designadores de Eventos

...

! Inicie uma substituição do histórico, exceto quando seguido por um espaço em branco, nova linha, retorno de carro, = ou ((quando a opção extglob shell estiver ativada usando o shopt interno).

...

!! Consulte o comando anterior. Este é um sinônimo para `! -1 '.

...

Designadores de Palavras

...

$ A última palavra. Normalmente, esse é o último argumento, mas será expandido para a palavra zero, se houver apenas uma palavra na linha.

...

Se um designador de palavras for fornecido sem uma especificação de evento, o comando anterior será usado como o evento.

JoL
fonte
Você está assumindo o título da pergunta muito literalmente. (OK, é um título ruim.) Todos podemos ver que o comando « export EDITOR="emacs -nw"» consiste em duas palavras: a primeira é « export» e a segunda é « EDITOR="emacs -nw"». A questão é realmente perguntar: "O que a página de manual do bash e o Manual do Bash significam quando dizem que !_'se expande para o último argumento do comando anterior', já que o bash é definido $_como" EDITOR"neste caso?" Copiar e colar a seção da página de manual do bash na expansão do histórico não é particularmente útil.
G-Man diz 'Reinstate Monica'