bash扩展测试语法中的重复否定(!)运算符不会互相否定?

Swa*_*dog 6 bash

所以这比我真正想要使用的东西更加奇怪.但我发现了一些我用bash扩展测试语法无法理解的东西.

检查一下(包括我的shell版本以防万一):

34>$SHELL --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)
Copyright (C) 2007 Free Software Foundation, Inc.
35>[ ! -d /tmp ] && echo Hi
36>[ ! ! -d /tmp ] && echo Hi
Hi
37>[[ ! -d /tmp ]] && echo Hi
38>[[ ! ! -d /tmp ]] && echo Hi
39>
Run Code Online (Sandbox Code Playgroud)

好的,所以第35和36行使用正常测试,按照我的预期运行.单个爆炸不会打印一条线(因为/ tmp存在),双重爆炸.

第37行,使用扩展的bash语法,也没有打印任何东西,正如我所料.但第38行也没有!这对我来说是令人惊讶的; 它表示该目录不存在,但也不存在?

搜索有关这方面的信息令人沮丧.我在这里错过了什么吗?一个未提及的语法错误?我只是想明白为什么会这样.

Add*_*son 1

由于在 BASH 代码中使用了一个标志,在这种特殊情况下不会切换,只有第一个实例很!重要,除非括号将它们分开。

为了找到这一点,我首先查看了Bash(1) 手册页

保留字

保留字是对 shell 有特殊含义的字。[...] 未加引号的单词被识别为保留字,并且是简单命令的第一个字(参见下面的 SHELL GRAMMAR)或 case 或命令的第三个字

这暗示了保留字的用法!

然而,这并不能很好地解释它。所以我查看了Bash 的源代码(版本 4.4.18)

当它检测到符号时,它看起来command.h包含一个为命令设置的标志!

#define CMD_INVERT_RETURN  0x04 /* Invert the exit value. */
Run Code Online (Sandbox Code Playgroud)

该文件在文件中多次使用execute_cmd.c

invert = (command->flags & CMD_INVERT_RETURN) != 0;
Run Code Online (Sandbox Code Playgroud)

但这个文件似乎只是检查标志是否存在。我相信它的解析是在另一个文件中完成的。

在 的第 4507 行parse.y,我们可以看到它似乎只是被设置,而不是被切换。这意味着无论 BANG ( !) 出现多少次,它只会设置该标志一次。

else if (tok == BANG || (tok == WORD && (yylval.word->word[0] == '!' && yylval.word->word[1] == '\0'))) {
  if (tok == WORD)
dispose_word (yylval.word); /* not needed */
  term = cond_term ();
  if (term)
term->flags |= CMD_INVERT_RETURN;
}
Run Code Online (Sandbox Code Playgroud)

我发现这种行为很奇怪,因为在代码的后面,支持切换这个值,在 的第 1201 行parse.y,它与管道相关(格式化以提高可读性)

pipeline_command: pipeline
{ $$ = $1; }            
    | BANG pipeline_command {
        if ($2)
            $2->flags ^= CMD_INVERT_RETURN; /* toggle */
        $$ = $2;
    }
Run Code Online (Sandbox Code Playgroud)