Qual è l'ultimo argomento del comando precedente?


12

$_ si dice che sia l'ultimo argomento del comando precedente.

Quindi mi chiedo perché non lo sia, EDITOR="emacs -nw"ma EDITORnel seguente esempio?

Perché non fa "emacs -nw"parte dell'ultimo argomento?

Più in generale, quali sono le definizioni di un argomento e l'ultimo argomento?

Grazie.

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

3
Penso che sia per lo stesso motivo che shellcheck ti dice di non esportare variabili sulla stessa riga che le assegni. L'assegnazione avviene e quindi la variabile viene esportata. EDITORè un argomento da esportare
jesse_b

FWIW, pdkshe dashincluderà il valore che è stato assegnato, ma ksh93si comporterà così bash.
Kusalananda

zsh:, export FOO=bar; echo $_stampe export.
ilkkachu,

@Jesse_b L'intero argomento è l'ultimo argomento / operando (incluso il valore assegnato), ma potrebbe avere qualcosa a che fare con il fatto che si exporttratta di un'utilità integrata.
Kusalananda

ksh: typeset -x FOO=barquindi echo $_stampa FOO, ma in declare -x FOO=bar; echo $_stampe Bash FOO=bar.
ilkkachu,

Risposte:


13

Bash elabora le assegnazioni delle variabili, quando sono ammessi come argomenti (con alias, declare, export, local, readonly, e typeset), prima di ogni altra cosa (o meglio, li identifica prima di ogni altra cosa - di espansione si applica ai valori assegnati alle variabili). Quando si arriva all'espansione delle parole, il comando rimanente è export EDITOR, quindi _è impostato su EDITOR.

In generale, gli argomenti sono le "parole" rimaste dopo l'espansione (che non include assegnazioni di variabili e reindirizzamenti).

Vedi Espansione dei comandi semplice nel manuale di Bash per i dettagli.


E mi rendo conto che declareil comportamento non corrisponde a quello che sto descrivendo ...
Stephen Kitt,

Bene, non è molto coerente in questo. declare a=b; echo $_stampe a=b; export c=d; echo $_stampa solo c. aliassembra stampare solo il nome, locald'altra parte stampa l'intero argomento. E readonlystampa anche solo il nome, che ho trovato un po 'sorprendente poiché avrei pensato readonlye localsarebbe simile declare.
ilkkachu,

1
@ilkkachu heh, l'ho capito anch'io (vedi sopra). exporte readonlyvengono dichiarati insieme a setattr.def, declare, local, e typesetvengono dichiarati in declare.def, aliassi trova solo in alias.def.
Stephen Kitt,

Grazie. Quando le assegnazioni delle variabili vengono utilizzate come argomenti per alcuni comandi, (1) "(o meglio, le identifica prima di ogni altra cosa - l'espansione si applica ai valori assegnati alle variabili)", vuoi dire che l'espansione avviene ai valori prima di eseguire l'assegnazione delle variabili? (2) "Quando si arriva all'espansione delle parole, il comando rimanente è export EDITOR", vuoi dire che l'assegnazione delle variabili avviene prima dell'espansione? Le due citazioni sembrano contraddirsi.
Tim

Grazie. Sono leggermente confuso. Quando le assegnazioni di variabili vengono utilizzate come argomenti per alias, declare, export, local, readonlye typeset. Cosa succede prima e dopo? "Quando si arriva all'espansione delle parole, il comando rimanente è export EDITOR", implica che l'assegnazione delle variabili EDITOR="emacs -nw"avviene prima dell'espansione? In caso contrario, perché il comando rimanente non contiene l'assegnazione come argomento? Se sì, l'espansione dei valori assegnati alle variabili non deve avvenire prima di eseguire l'assegnazione delle variabili?
Tim

4

TL; DR: Nel caso di export FOO=bar, bash invoca la sua creazione di ambiente temporaneo, imposta FOO=barin quell'ambiente, quindi produce un comando finale di export FOO. A quel punto, FOOviene preso come ultimo argomento.


Ah, il tanto abusato $_:

($ _, trattino di sottolineatura). All'avvio della shell, impostare sul percorso assoluto usato per invocare la shell o lo script della shell in esecuzione come passato nell'ambiente o nell'elenco degli argomenti. Successivamente, si espande all'ultimo argomento al comando precedente, dopo l'espansione. Impostato anche sul percorso completo utilizzato per richiamare ciascun comando eseguito e inserito nell'ambiente esportato in quel comando. Quando si controlla la posta, questo parametro contiene il nome del file di posta.

Diamo un'occhiata ad alcune varianti:

$ 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

Quindi vediamo tre schemi qui:

  • I comandi richiamati dal filesystem, dalle funzioni e dai built-in si comportano come generalmente previsto: $_è impostato sul nome del comando stesso se non ci sono argomenti, altrimenti l'ultimo degli argomenti presentati.
  • Dopo le definizioni delle funzioni, i loop e altre costruzioni logiche: $_non viene modificato.
  • Tutto il resto: $_è impostato su qualcosa di non del tutto previsto; strano.

Ho elaborato il codice per fornire alcune informazioni sulla stranezza.

$ ./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]

Si può vedere che il parser vede l'ultimo argomento atteso ( lastarg=) in tutti i casi, ma ciò che accade dopo dipende da ciò che pensa bash dovrebbe accadere. Vedi execute_cmd.c, execute_simple_command () .

Nel caso di export FOO=bar, bash esegue l'assegnazione e quindi esporta la variabile. Ciò sembra coerente con l'affermazione della documentazione secondo cui l'ultimo argomento è stato calcolato dopo l'espansione.


1
Come fa la shell a sapere che stai controllando la posta?
rackandboneman,

@rackandboneman senza confermare, sospetto che i controlli interni siano stati effettuati sulla base diMAILCHECK
Jeff Schaller

2

Per rispondere alla domanda sul titolo, prova !$:

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

Questa è l'espansione della storia. Dalla manpage di bash:

L'espansione della cronologia viene eseguita immediatamente dopo la lettura di una riga completa, prima che la shell la spezzi in parole. Si svolge in due parti. Il primo è determinare quale riga dell'elenco cronologico utilizzare durante la sostituzione. Il secondo è selezionare le parti di quella linea per l'inclusione in quella corrente. La linea selezionata dalla storia è l'evento e le parti di quella linea su cui si agisce sono le parole.

...

Designatori di eventi

...

! Inizia una sostituzione della cronologia, tranne quando seguito da uno spazio vuoto, una nuova riga, ritorno a capo, = o ((quando l'opzione della shell extglob è abilitata usando lo shopt builtin).

...

!! Fare riferimento al comando precedente. Questo è un sinonimo di `! -1 '.

...

Designatori di parole

...

$ L'ultima parola. Questo di solito è l'ultimo argomento, ma si espanderà alla parola zeroth se c'è solo una parola nella riga.

...

Se viene fornito un identificatore di parole senza una specifica dell'evento, come evento viene utilizzato il comando precedente.


Stai prendendo il titolo della domanda waaaaay troppo alla lettera. (OK, è un titolo scadente.) Tutti possiamo vedere che il comando « export EDITOR="emacs -nw"» è composto da due parole: la prima è « export» e la seconda è « EDITOR="emacs -nw"». La domanda è davvero: "Che cosa significano la pagina man di bash e il manuale di Bash quando dicono che !_" si espande all'ultimo argomento al comando precedente ", dato che bash si imposta $_su" EDITOR"in questo caso?" Copiare e incollare la sezione della pagina man bash sull'espansione della cronologia non è particolarmente utile.
G-Man dice "Reinstate Monica" il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.