$_
据说是前一个命令的最后一个参数。
所以我想知道为什么不是,EDITOR="emacs -nw"
但EDITOR
在下面的例子中呢?
为什么不是"emacs -nw"
最后一个参数的一部分?
更一般地说,一个参数和最后一个参数的定义是什么?
谢谢。
$ export EDITOR="emacs -nw"
$ echo $_
EDITOR
答案1
当变量赋值被允许作为参数(使用alias
、declare
、export
、local
、readonly
和typeset
)时,Bash 会在其他任何事情之前处理变量赋值(或者更确切地说,它在其他任何事情之前识别它们 - 扩展适用于分配给变量的值)。当进行单词扩展时,剩下的命令是export EDITOR
,所以_
设置为EDITOR
。
一般来说,参数是扩展后剩余的“单词”(不包括变量赋值和重定向)。
看简单的命令扩展有关详细信息,请参阅 Bash 手册。
答案2
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 进行赋值,然后导出变量。这似乎与文档中最后一个参数在扩展后计算的断言一致。
答案3
要回答标题问题,请尝试!$
:
$ export EDITOR="emacs -nw"
$ echo !$
EDITOR=emacs -nw
这是历史的扩展。来自 bash 联机帮助页:
读取完整行后,在 shell 将其分解为单词之前,立即执行历史扩展。它分为两部分。第一个是确定在替换期间使用历史列表中的哪一行。第二种是选择该行的部分内容以包含到当前行中。从历史中选择的行是事件,而该行中被执行的部分是单词。
...
事件指示符
...
!开始历史替换,除非后面跟有空格、换行符、回车符、= 或 ((当使用内置 shopt 启用 extglob shell 选项时)。
...
!!参考上一条命令。这是“!-1”的同义词。
...
词指示符
...
$ 最后一句话。这通常是最后一个参数,但如果行中只有一个单词,则会扩展到第零个单词。
...
如果提供的字指示符没有指定事件,则前一个命令将用作事件。