为什么在ksh中“ [[”和“ -e xxx”之间需要空格?


9

例如,以下命令不起作用:

if [[-e xyz]]; then echo File exists;fi

ksh给出以下错误

[[-e: command not found

那是因为“ [[-”不明确吗?


2
[[是一个关键字 -可能是外壳的解析树要求关键字必须由空格划定
-steeldriver,

另一种看待它的方式可能是您可以编写一个函数[[-,shell将如何解析“在[[]上执行e标志而不是在函数[[-在e上执行]”。
pbhj

Answers:


15

最简单的解释是,因为在手册中显示为[[ expression ]]如此,必须有之间的空间[[expression]]。但是我们当然可以尝试更深入地研究它。Shell的语法有些复杂,并且严重依赖“单词拆分”的概念。特别是, ksh(1)手册指出:

Shell通过将其分解为单词来开始解析其输入。单词是字符序列,由无引号的空格字符(空格,制表符和换行符)或元字符(<,>,|,;,&,(和))分隔。除了分隔单词外,空格和制表符也被忽略,而换行符通常分隔命令。

因此,如手册中所述,字符序列[[-e被认为是一个空壳词。ksh会在内置函数和特殊运算符(如forwhile)的列表中查找此类命令,然后查找外部命令-和voilà-未找到,因此出现有关未找到命令的错误消息。

在这种情况下[[,也不是特殊的元字符。如果是这样,那么其中的-e部分[[-e将被视为一个空壳词,而不是其[[自身的参数。但是,[[被描述为复合命令,并且该手册中指出以下内容:

复合命令是使用以下保留字创建的-仅在未引号且将它们用作命令的第一个字的情况下,这些字才被识别(即,它们不能以参数赋值或重定向开头):

因此,为了[[被识别为复合命令,它必须是命令或命令列表的第一个单词,根据先前对“单词”的定义,这意味着它必须用空格,制表符或换行符分隔单词/参数。


您已经在注释中提出了疑问:“为什么解析器一旦找到“ [[”模式并将其视为条件命令的开始,就无法停止?” 简短的答案可能是因为1)[语法的影响,因为它最初是一个外部命令,并且对于POSIX标准,即使在今天,依从性仍然作为外部命令存在;以及2)因为shell解析器是这样构建的。Shell解析器可以识别其他非空格分隔的特殊字符: echo $((2+2))并且(echo foobar)工作得很好。也许将来ksh开发恢复时,或者似乎出现了fork或克隆(如mkshpdksh)时,有人会在中实现无空间语法[[-e

也可以看看:


3
我认为ksh手册报价确实是当场。它类似于任何其他编程语言:在C中,char是一个关键字,但是您仍然无法编写代码,charsomeletter = 'A';并且希望解析器在看到之后会停止char
彼得-恢复莫妮卡

我认为问题中的“ char”示例和符号“ [[”之间的主要区别在于,通常,字母数字标识符需要使用空格分隔符来分隔;另一方面,使用特殊符号作为令牌,不需要空格分隔符。
AcBap19年

究竟。我不确定与C的比较是否有帮助。用C语言可以说i--<=++j,编译器毫无疑问地将其解析为i -- <= ++ j
斯科特,

@Scott我认为C比较会有所帮助(C标识符仅允许使用字母数字和有点次要_)。Ksh具有(作为运算符,并(echo hello)解析为( echo hello )。它if{[[,等关键字,和ifx{x[[x是每一个令牌-怎么样charsomeletter是一种C标记。相比之下,(xksh中未加引号的是两个令牌-就像i--C中的两个令牌一样。Bourne样式的shell(如ksh)和C之间在概念上甚至意味着要成为运算符也不同。但是Peter A. Schneider指出真正的词汇相似性。
伊利亚·卡根

2

需要注意的重要一点是这[是一个命令,[[bash和ksh中的shell关键字基于[

[[的用法与相似[有时,您甚至可以替换为[ ][[ ]]而无需改变行为。使用[和任何命令一样,该命令及其参数之间必须有一个空格。同样适用于[[

我们曾经只有/usr/bin/[。现在,大多数shell都是[为了提高效率而内置的-但语法是相同的。在提供的外壳中[[,它可以作为通用的替代品[

这是[bash中的描述:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

因此[等于test(除了在最后期望]参数之外)。help test将提供更多细节。您可以将此与进行比较help [[

外部命令[man \[)也有一个手册页。

如果是

if [[-e
  • 那里也[没有[[命令。完整的词[[-e是。
  • if [[-e使其成为对/错的测试。那么命令是否[[-e存在?

那是因为“ [[-”不明确吗?

是。或者没有。[[-e就是这样:shell无法理解任何内容,因此假定它是自己的命令。;-)


“%help [[”表示“ [[”是一个条件命令。为什么解析器发现“ [[”模式后就立即停止,并将其视为条件命令的开始?
AcBap19年

@Myloti [[-e是一个可能有效的(如果看起来很奇怪)命令名称。
Gordon Davisson

2

该空间是分隔符,是必需的。如您所见shellcheck

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(ksh和bash都支持,[[并且没有空格就无法工作。Shellcheck会使用包含该错误行的类似的ksh和bash脚本准确地提供该输出。)

为什么需要分隔符与标记和词典有关


请注意,OP ksh在问题中询问了有关shell的问题。巴什借用[[操作者ksh和在这个问题上他们的行为是相同的,但有一些之间的差异显著我会谨慎的假设kshbash行为和内部结构。那仅仅是因为shellcheck在bash脚本上工作,它不一定是ksh脚本的合适工具。接近不同炮弹时要记住的事情
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy我在使用shellcheck突出显示[[内置规则时表现出机会。我绝不推断这ksh是一个shellcheck可以发现错误的外壳。我希望其他人会喜欢ksh并且bash是两个不同的解释器。谢谢你提到那件事。还值得注意的[[是内置的bash,而[外部命令则是。
WinEunuuchs2Unix

实际上,[它也是bash的内置功能:) manpages.ubuntu.com/manpages/bionic/man7/bash-builtins.7.html但是您相距不远- [test需要作为外部命令。在最初的Bourne shell时代[,实际上是一个外部命令,并且/usr/bin/[由于POSIX要求它是一个外部命令,因此如今仍然存在,它是pubs.opengroup.org/onlinepubs/009695399/utilities/test.html POSIX几乎不需要内置pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html 内置功能[是为了提高效率
Sergiy Kolodyazhnyy

2
Shellcheck知道ksh; 使用hashbang或-s ksh。顺便说一句,KSH和bash有[[“中建”,但它是一个关键词,不像[,这是一个shell内建。(ksh :; whence -v [ [[bash type [ [[:)内置命令和外部命令在语法上相似。一种方式[[不同于[的是,[[抑制了它的参数,它不能作为一个内置了一些扩展-就像{不能做分组是它内置的。这可能就是为什么{x(或[[x)作为一个令牌感到奇怪的原因。我认为说内置函数和关键字在词法相似但在语法不相似是正确的。@SergiyKolodyazhnyy
Kagan

1
@EliahKagan实际上,我已经在U&L上发布了一个问题,以准确了解POSIX对这种情况的 看法unix.stackexchange.com/questions/526574/…Shell 语言足够复杂,希望有人可以用更正式的方式进行解释
Sergiy Kolodyazhnyy

1

[[可以是外部命令!也就是说,它可能是一个程序,而不是外壳程序直接支持的某些语法。支持无空间语法是可能的,ksh但在具有外部系统的系统上可能会失败,[[因此出于兼容性原因,最好保留所需的空间。

例如,BusyBox [[作为外部命令提供


0

由于[[-e可以参与shell扩展(它可以像

echo [[-e]*

为了列出所有以“。” [和“。” 之间的字母开头的文件e,如果[]是特殊字符不参与正常的空白字分割,那将是一团糟。

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.