当我运行这两个命令时,我得到
$ type cd
cd is a shell builtin
$ type if
if is a shell keyword
清楚地显示出它cd
是一个内置的shell,并且if
是一个shell关键字。那么shell内置和关键字之间有什么区别?
当我运行这两个命令时,我得到
$ type cd
cd is a shell builtin
$ type if
if is a shell keyword
清楚地显示出它cd
是一个内置的shell,并且if
是一个shell关键字。那么shell内置和关键字之间有什么区别?
Answers:
Bash解析代码的方式与内置关键字之间有着很大的区别。在讨论区别之前,让我们列出所有关键字和内建函数:
内建函数:
$ compgen -b
. : [ alias bg bind break
builtin caller cd command compgen complete compopt
continue declare dirs disown echo enable eval
exec exit export false fc fg getopts
hash help history jobs kill let local
logout mapfile popd printf pushd pwd read
readarray readonly return set shift shopt source
suspend test times trap true type typeset
ulimit umask unalias unset wait
关键字:
$ compgen -k
if then else elif fi case
esac for select while until do
done in function time { }
! [[ ]] coproc
注意,例如,它[
是一个内置[[
函数,并且是一个关键字。我将使用这两个示例来说明以下区别,因为它们是众所周知的运算符:每个人都知道它们并定期(或应该)使用它们。
Bash在解析关键字的早期就对其进行了扫描和理解。例如,这允许以下操作:
string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
echo "The string is non-empty"
fi
这工作正常,Bash会愉快地输出
The string is non-empty
请注意,我没有引用$string_with_spaces
。而以下内容:
string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
echo "The string is non-empty"
fi
表明Bash不满意:
bash: [: too many arguments
为什么它只适用于关键字而不适用于内置函数?因为当Bash解析代码时,它会看到[[
哪个是关键字,并且很早就知道它是特殊的。因此,它将查找结束]]
并以特殊方式处理内部。内置命令(或命令)被视为要使用参数调用的实际命令。在最后一个示例中,bash理解应[
使用带参数的命令运行命令(每行显示一个):
-n
some
spaces
here
]
由于发生了变量扩展,引号删除,路径名扩展和单词拆分。该命令[
原来是构建在shell中的,因此它将使用这些参数执行该命令,这将导致错误,从而引起投诉。
在实践中,您会看到这种区别允许复杂的行为,而内置函数(或命令)则无法实现。
仍然在实践中,如何区分内置关键字和关键字?这是一个有趣的实验,可以执行:
$ a='['
$ $a -d . ]
$ echo $?
0
当Bash解析该行时$a -d . ]
,它看不到任何特殊的东西(即,没有别名,没有重定向,没有关键字),因此它只是执行变量扩展。变量展开后,它将看到:
[ -d . ]
所以执行该命令(内建)[
与参数-d
,.
以及]
,当然,为真(这仅测试是否.
是目录)。
现在看:
$ a='[['
$ $a -d . ]]
bash: [[: command not found
哦。这是因为当Bash看到此行时,它看不到任何特殊之处,因此展开了所有变量,最终看到了:
[[ -d . ]]
目前,别名扩展和关键字扫描已执行了很长时间,并且不再执行了,因此Bash尝试查找名为的命令[[
,但找不到它,然后抱怨。
同样:
$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found
和
$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found
别名扩展也很特别。您已经至少完成了以下一次操作:
$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found
原因是相同的:别名扩展发生在变量扩展和引号删除之前。
关键字与别名
现在,如果我们将别名定义为关键字,您会怎么想?
$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0
哦,行得通!因此,别名可以用作关键字的别名!很高兴知道。
结论:内建函数实际上就像命令一样:它们对应于使用参数执行的动作,该参数经过直接变量扩展以及分词和globing。确实就像在某个地方有一个外部命令,/bin
或者/usr/bin
用变量扩展等后给出的参数调用它。请注意,当我说这就像拥有一个外部命令时,我的意思仅是关于参数,单词拆分,通配符,变量扩展等。内建函数可以修改外壳的内部状态!
另一方面,关键字是很早就被扫描和理解的,并允许复杂的外壳行为:外壳将能够禁止分词或扩展路径名等。
现在查看内建函数和关键字的列表,并尝试找出为什么某些关键字需要使用。
!
是一个关键字。似乎可以通过以下功能模仿其行为:
not() {
if "$@"; then
return false
else
return true
fi
}
但这会禁止类似的构造:
$ ! ! true
$ echo $?
0
要么
$ ! { true; }
echo $?
1
相同time
:具有关键字的功能更强大,以便它可以为带有重定向的复杂复合命令和管道计时:
$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null
如果time
其中一个单纯的命令(甚至是内置),也只是看到了争论grep
,^#
和/home/gniourf/.bashrc
,这个时间,然后它的输出会去通过管道的剩余部分。但是使用关键字,Bash可以处理一切!它可以time
完成整个管道,包括重定向!如果time
仅仅是一个命令,我们就不能做:
$ time { printf 'hello '; echo world; }
试试吧:
$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'
尝试修复(?):
$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory
绝望。
关键字与别名?
$ alias mytime=time
$ alias myls=ls
$ mytime myls
您认为会发生什么?
实际上,内建函数类似于命令,只是它内置在外壳中,而关键字是允许复杂行为的东西!我们可以说这是外壳语法的一部分。
=\
“使用man
,apropos
和help
我已经没有任何运气。知道我在哪里可以找到该信息吗?通常,以便将来我可以看到其中的其他内容,因为我认为我缺少参考资料。
\ll
,"ll"
或'll'
。
[[
yield \[[
,所以不会将其解析为别名。到目前为止正确吗?我迷路的地方不是意识到反斜杠是Bash中的引号,我试图在别名和关键字下查找它,并完全迷路了……一切都很好。在“报价”部分下:>非引号反斜杠()是转义字符。
\[[
标记化,并且是类型为LiteralQuoteToken的单个标记,与之相对[[
应的是OpenTestKeywordToken,并且需要使用封闭的]]
CloseTestKeywordToken进行oroper编译/正确语法。稍后,在(4)中,将LiteralQuoteToken评估[[
为要执行的bulitin /命令的名称,在(6)中,由于没有[[
内置命令或内置命令,Bash将拒绝。真好 随着时间的流逝,我可能会忘记确切的细节,但是现在Bash运行东西的方式对我来说更加清晰了。谢谢。
man bash
打电话给他们SHELL BUILTIN COMMANDS
。因此,“ shell内置”就像普通的命令,诸如此类grep
,但是它不是包含在单独的文件中,而是内置在bash本身中。这使它们比外部命令更有效地执行。
甲关键字也是“硬编码到击,但不像一个内建,关键字本身并不是一个命令,但命令构建体的亚基。” 我将其解释为意味着关键字本身没有功能,但是需要命令才能执行任何操作。(从链接,其他的例子是for
,while
,do
,和!
,还有更多的我的回答对您的其他问题。)
[[ is a shell keyword
但是[ is a shell builtin
。我不知道为什么。
[
早在今天就作为单独的命令存在。[[
没有由标准指定,因此开发人员可以选择将其作为关键字或内置。
Ubuntu随附的命令行手册未给出关键字的定义,但是在线手册(请参见旁注)和POSIX Shell命令语言标准规范将它们称为“保留字”,并且都提供了这些字词的列表。根据POSIX标准:
仅当不引用任何字符且该词用作以下内容时,才发生这种识别:
命令的第一个字
除大小写,for或in之外的保留字之一之后的第一个字
case命令中的第三个单词(在这种情况下,仅in有效)
for命令中的第三个单词(在这种情况下,仅in和do有效)
这里的关键是关键字/保留字具有特殊的含义,因为它们有助于shell语法,用于表示某些代码块,例如循环,复合命令,分支(if / case)语句等。它们允许形成命令语句,但是靠自己-不要做任何事情,实际上,如果您输入诸如for
,,之类的关键字until
,case
则-shell会期望使用完整的语句,否则-语法错误:
$ for
bash: syntax error near unexpected token `newline'
$
在源代码级别,bash的保留字在parese.y中定义,而内置文件则拥有专用于它们的整个目录。
GNU索引显示[
为保留字,但实际上是内置命令。[[
相比之下,是保留字。
另请参阅:关键字,保留字和内置关键字之间的区别?