前一个命令的最后一个参数是什么?


12

$_ 据说是上一个命令的最后一个参数。

所以我想知道为什么不是,EDITOR="emacs -nw"EDITOR在下面的示例中呢?

为什么不是"emacs -nw"最后一个论点的一部分?

更一般地,一个参数和最后一个参数的定义是什么?

谢谢。

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

3
我认为出于同样的原因,shellcheck告诉您不要在分配变量的同一行上导出变量。发生分配,然后导出变量。 EDITOR是出口的论点
jesse_b

FWIW,pdksh并且dash将包括已分配的值,但ksh93将表现为bash一样。
Kusalananda

zsh : export FOO=bar; echo $_,打印export
ilkkachu

@Jesse_b整个过程是最后一个参数/操作数(包括分配的值),但它可能与export内置实用程序有关。
Kusalananda

ksh:typeset -x FOO=bar然后echo $_打印FOO,但在Bash中declare -x FOO=bar; echo $_打印FOO=bar
ilkkachu

Answers:


13

巴什处理变量赋值,当他们可以作为变量(有aliasdeclareexportlocalreadonly,和typeset),任何事情之前(或者更确切地说,它之前别的识别它们-扩展应用到分配给变量的值)。进行单词扩展时,其余命令为export EDITOR,因此_设置为EDITOR

一般来说,参数是扩展后剩余的“单词”(不包括变量分配和重定向)。

有关详细信息,请参见Bash手册中的简单命令扩展


我意识到declare自己的行为与我所描述的不符...
Stephen Kitt

好吧,这并不是很一致。declare a=b; echo $_版画a=b; export c=d; echo $_只打印calias似乎只打印名称,local另一方面打印整个参数。并readonly仅打印名称,这让我感到有些惊讶,因为我本来会以为 readonly并且local与相似declare
ilkkachu

1
@ilkkachu呵呵,我也意识到了(见上文)。exportreadonly在声明一起setattr.defdeclarelocal,和typeset中宣布declare.defalias独站alias.def
史蒂芬·基特

谢谢。当变量赋值用作某些命令的参数时,(1)“(或者更确切地说,它在其他任何事物之前都可以识别它们-扩展适用于分配给变量的值)”,您是否表示在执行变量赋值之前会对值进行扩展?(2)“谈到单词扩展时,剩下的命令是export EDITOR”,您是说在扩展之前执行变量分配吗?这两个引号似乎相互矛盾。
蒂姆(Tim)

谢谢。我有点困惑。当变量赋值被用作参数alias, declare, export, local, readonlytypeset。接下来会发生什么?“到单词扩展时,剩下的命令是export EDITOR”,您是否暗示变量分配EDITOR="emacs -nw"在扩展之前发生?如果不是,为什么其余命令不包含赋值作为参数?如果是,执行变量分配之前是否不必对分配给变量的值进行扩展?
蒂姆(Tim)

4

TL; DR:对于export FOO=bar,bash调用其临时环境创建,FOO=bar在该环境中进行设置,然后产生的最终命令export FOO。在这一点上,FOO作为最后一个参数。


啊,被滥用的$_

($ _,下划线。)在shell启动时,设置为用于调用在环境或参数列表中传递的正在执行的shell或shell脚本的绝对路径名。随后,在扩展后,扩展到上一个命令的最后一个参数。还设置为用于调用每个执行的命令的完整路径名,并将其放置在导出到该命令的环境中。检查邮件时,此参数保存邮件文件的名称。

让我们看一些变化:

$ 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

因此,我们在这里看到三种模式:

  • 从文件系统,函数和内置函数调用的命令的行为与通常预期的一样:$_如果没有参数,则设置为命令名称本身,否则设置为最后出现的参数。
  • 在函数定义,循环和其他逻辑构造之后:$_不会被修改。
  • 其他一切:$_设置为不太期望的值;奇怪的。

我已经对代码进行了检测,以提供一些有关怪异的见解。

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

您可以看到在所有情况下解析器都可以看到预期的最后一个参数(lastarg=),但是此后发生的情况取决于bash认为应该发生的情况。请参阅execute_cmd.c,execute_simple_command()

对于export FOO=bar,bash进行赋值,然后导出变量。这似乎与文档的主张(即扩展后计算的最后一个参数)一致。


1
Shell如何知道您正在检查邮件?
rackandboneman '18 -4-12

@rackandboneman未经证实,我怀疑基于MAILCHECK
Jeff Schaller

2

要回答标题问题,请尝试!$

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

这是历史的扩展。从bash手册页:

读完整行后,shell将其分解为单词之前,将立即执行历史记录扩展。它分为两个部分。首先是确定在替换过程中要使用的历史记录列表中的哪一行。第二步是选择该行的一部分以包含在当前行中。从历史记录中选择的行是事件,行中作用的部分是单词。

...

活动代号

...

!开始历史记录替换,除非后面跟一个空白,换行符,回车符,=或((使用内置的shopt启用extglob shell选项时)。

...

!! 请参考上一条命令。这是`!-1'的同义词。

...

文字代号

...

$硬道理。这通常是最后一个参数,但是如果行中只有一个单词,则会扩展到第0个单词。

...

如果提供的单词指示符没有事件说明,则将先前的命令用作事件。


您从字面上也正题为waaaaay。(好吧,这是一个很差的标题。)我们都可以看到命令« export EDITOR="emacs -nw"»由两个单词组成:第一个是« export»,第二个是« EDITOR="emacs -nw"»。问题确实是在问:“ 在这种情况下!_,如果bash设置$_为« EDITOR»,那么bash手册页和Bash手册说'扩展到上一个命令的最后一个参数' 是什么意思?” 复制和粘贴有关历史记录扩展的bash手册页部分并不是特别有帮助。
G-Man说'Resstate Monica''Apr
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.