¿Cuál es el último argumento del comando anterior?

12

$_ se dice que es el último argumento del comando anterior.

Entonces, me pregunto por qué no lo es, EDITOR="emacs -nw"pero EDITORen el siguiente ejemplo.

¿Por qué no es "emacs -nw"parte del último argumento?

En términos más generales, ¿cuáles son las definiciones de un argumento y el último argumento?

Gracias.

$ export EDITOR="emacs -nw"
$ echo $_
EDITOR
Tim
fuente
3
Creo que es por la misma razón que Shellcheck le dice que no exporte variables en la misma línea que las asigna. La asignación ocurre y luego se exporta la variable. EDITORes un argumento para exportar
jesse_b
FWIW pdkshe dashincluirá el valor asignado, pero ksh93se comportará como lo bashhace.
Kusalananda
zsh:, export FOO=bar; echo $_impresiones export.
ilkkachu
@Jesse_b Todo el asunto es el último argumento / operando (incluido el valor asignado), pero puede tener algo que ver con el hecho de que exportes una utilidad incorporada.
Kusalananda
ksh: typeset -x FOO=barluego echo $_imprime FOO, pero en Bash declare -x FOO=bar; echo $_imprime FOO=bar.
ilkkachu

Respuestas:

13

Bash procesa las asignaciones de variables, cuando se les pueden utilizarse como argumentos (con alias, declare, export, local, readonly, y typeset), antes que nada (o más bien, que los identifica ante todo - la expansión se aplica a los valores asignados a las variables). Cuando se trata de la expansión de palabras, el comando restante es export EDITOR, por lo que _se establece en EDITOR.

En términos generales, los argumentos son las "palabras" que quedan después de la expansión (que no incluye asignaciones variables y redirecciones).

Vea Expansión de comandos simples en el manual de Bash para más detalles.

Stephen Kitt
fuente
Y me di cuenta declare's comportamiento no coincide con lo que estoy describiendo ...
Stephen Kitt
Bueno, no es muy consistente en eso. declare a=b; echo $_impresiones a=b; export c=d; echo $_impresiones solo c. aliasparece imprimir solo el nombre, localpor otro lado imprime todo el argumento. Y readonlytambién imprime solo el nombre, lo que me pareció un poco sorprendente ya que habría pensado readonlyy localsería similar a declare.
ilkkachu
1
@ilkkachu heh, me di cuenta de eso también (ver arriba). exporty readonlyse declaran juntos en setattr.def, declare, local, y typesetse declaran en declare.def, aliasse encuentra solo en alias.def.
Stephen Kitt
Gracias. Cuando las asignaciones de variables se usan como argumentos para algunos comandos, (1) "(o más bien, los identifica antes que nada: la expansión se aplica a los valores asignados a las variables)", ¿quiere decir que la expansión ocurre a los valores antes de realizar la asignación de variables? (2) "Cuando se trata de la expansión de palabras, el comando restante es exportar EDITOR", ¿quiere decir que la asignación de variables ocurre antes de la expansión? Las dos citas parecen contradecirse entre sí.
Tim
Gracias. Estoy un poco confundido. Cuando las asignaciones de variables se usan como argumentos para alias, declare, export, local, readonlyy typeset. ¿Qué pasa primero y después? "Cuando se trata de la expansión de palabras, el comando restante es export EDITOR", ¿implica que la asignación de variables EDITOR="emacs -nw"ocurre antes de la expansión? Si no, ¿por qué el comando restante no contiene la asignación como argumento? En caso afirmativo, ¿no tiene que ocurrir la expansión de los valores asignados a las variables antes de realizar la asignación de variables?
Tim
4

TL; DR: en el caso de export FOO=bar, bash invoca la creación de su entorno temporal, se establece FOO=baren ese entorno y luego produce un comando final de export FOO. En ese punto, FOOse toma como último argumento.


Ah, el muy maltratado $_:

($ _, un guión bajo). Al iniciar el shell, establezca el nombre de ruta absoluto utilizado para invocar el shell o el script de shell que se ejecuta como se pasa en el entorno o la lista de argumentos. Posteriormente, se expande hasta el último argumento del comando anterior, después de la expansión. Establezca también el nombre de ruta completo utilizado para invocar cada comando ejecutado y colocado en el entorno exportado a ese comando. Al verificar el correo, este parámetro contiene el nombre del archivo de correo.

Veamos algunas variaciones:

$ 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

Entonces vemos tres patrones aquí:

  • Los comandos invocados desde el sistema de archivos, las funciones y las funciones incorporadas se comportan como generalmente se espera: $_se configura con el nombre del comando en sí mismo si no hay argumentos; de lo contrario, el último de los argumentos presentados.
  • Después de las definiciones de funciones, bucles y otras construcciones lógicas: $_no se modifica.
  • Todo lo demás: $_se establece en algo que no se espera del todo; extraño.

He instrumentado el código para proporcionar una idea de la rareza.

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

Puede ver que el analizador ve el último argumento esperado ( lastarg=) en todos los casos, pero lo que sucede a partir de entonces depende de lo que bash piense que debería suceder. Ver execute_cmd.c, execute_simple_command () .

En el caso de export FOO=bar, bash realiza la asignación y luego exporta la variable. Esto parece coherente con la afirmación de la documentación de que el último argumento se calculó después de la expansión.

obispo
fuente
1
¿Cómo sabe el shell que está revisando el correo?
rackandboneman
@rackandboneman sin confirmar, sospecho que las comprobaciones internas se basan enMAILCHECK
Jeff Schaller
2

Para responder la pregunta del título, intente !$:

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

Esta es la expansión de la historia. Desde la página de manual de bash:

La expansión del historial se realiza inmediatamente después de leer una línea completa, antes de que el shell la divida en palabras. Se lleva a cabo en dos partes. El primero es determinar qué línea de la lista de historial usar durante la sustitución. El segundo es seleccionar partes de esa línea para incluirlas en la actual. La línea seleccionada del historial es el evento, y las partes de esa línea sobre las que se actúa son palabras.

...

Designadores de eventos

...

! Inicie una sustitución del historial, excepto cuando le siga un espacio en blanco, una nueva línea, un retorno de carro, = o ((cuando la opción de shell extglob está habilitada usando el shopt incorporado).

...

!! Consulte el comando anterior. Este es un sinónimo de `! -1 '.

...

Designadores de palabras

...

$ La última palabra. Este suele ser el último argumento, pero se expandirá a la palabra cero si solo hay una palabra en la línea.

...

Si se suministra un designador de palabra sin una especificación de evento, el comando anterior se usa como evento.

JoL
fuente
Estás tomando el título de la pregunta muuuuy demasiado literalmente. (OK, es un título pobre.) Todos podemos ver que el comando « export EDITOR="emacs -nw"» consta de dos palabras: la primera es « export» y la segunda es « EDITOR="emacs -nw"». La pregunta realmente es: "¿Qué significan la página de manual de bash y el Manual de Bash cuando dicen que !_'se expande hasta el último argumento del comando anterior', dado que bash se establece $_en« EDITOR»en este caso? Copiar y pegar la sección de la página man de bash sobre la expansión del historial no es particularmente útil.
G-Man dice 'reinstalar a Mónica' el