为什么像 [ $var == 'str' ] 这样的 zsh 代码作为命令运行良好,但作为脚本文件却出错?

Why the zsh codes like [ $var == 'str' ] runs well as a command but error as a script file?

zsh_test.sh很简单,如下:

#!/usr/bin/env zsh

if [ $USER == 'root' ]; then
    echo "root"
else
    echo "not root"
fi

恐怕您使用的测试命令有误。让我们看看为什么。

test 命令是从 Unix 版本 III 开始定义的。您经常可以在您的 PATH 中找到此命令也作为 [ 二进制文件。在大多数现代 shell 中(假设 bash 也是现代 shell),也有 test[ 作为内置命令的实现。根据规范,比较两个字符串的唯一有效方法是:

       STRING1 = STRING2
              the strings are equal

       STRING1 != STRING2
              the strings are not equal

Original strict POSIX test 命令的实现在某种程度上受到限制,可能难以使用。但它是便携的,这是它的主要优势。但是,如果您根本不关心可移植性怎么办?然后是条件表达式.

条件表达式,可作为 [[ 内置命令使用,已得到改进,而不是 POSIX 原始测试命令的兼容替代品。在 the manual 中查找可以与他们比较的内容以了解想法。还支持双等号 (==)(文档明确表示这是为了与其他类型的计算机语言兼容。)

结论?

当您为特定 shell 编写脚本时,例如 zsh,并且您绝对确定可移植性对您来说并不重要,请始终使用 [[ 而不是 [。你的生活会更轻松,对脚本的更改也很少:

#!/usr/bin/env zsh

if [[ $USER == 'root' ]]; then
    echo "root"
else
    echo "not root"
fi

如果需要在不同的 shell 和环境之间进行移植,您将不得不使用原始的 test[ 命令,而忘记 zsh、== 和很多其他的东西。

#!/bin/sh

if [ "$USER" = 'root' ]; then
    printf '%s\n' "root"
else
    printf '%s\n' "not root"
fi

我现在知道出了什么问题:您是一个相当晦涩的 zsh 机制的受害者,该机制在 zshexpn 手册页中有描述,称为 '= ' 扩展。来自手册页:

If a word begins with an unquoted `=' and the EQUALS option is set, the remainder of the word is taken as the name of a command. If a command exists by that name, the word is replaced by the full pathname of the command.

你可以用命令试试

echo ==

也输出此错误消息。例如,在我的 platofm

echo =ruby

输出 /usr/bin/ruby,因为这是我安装 ruby 的地方。如果您的 PATH 中有一个名为 = 的程序,== 将解析为该路径。

虽然在 [ ... ] 中使用双 == 符号是不常见的,但此命令的 zsh 实现允许这样做,但您必须引用运算符,以避免 =-expansion:

if [ $USER '==' root ]; then

另一种方法是使用 [[ ... ]]。这不是一个命令,而是一个句法结构,里面的扩展规则是不同的。因此

if [[ $USER == root ]]; then

也可以。