为什么像 [ $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
将上述代码复制并粘贴到 zsh shell,运行良好。
$ #!/usr/bin/env zsh
$ if [ $USER == 'root' ]; then
then> echo "root"
then> else
else> echo "not root"
else> fi
not root
但是直接执行脚本文件zsh_test.sh
,报错
$ ./zsh_test.sh
./zsh_test.sh:3: = not found
恐怕您使用的测试命令有误。让我们看看为什么。
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
也可以。
zsh_test.sh
很简单,如下:
#!/usr/bin/env zsh
if [ $USER == 'root' ]; then
echo "root"
else
echo "not root"
fi
将上述代码复制并粘贴到 zsh shell,运行良好。
$ #!/usr/bin/env zsh $ if [ $USER == 'root' ]; then then> echo "root" then> else else> echo "not root" else> fi not root
但是直接执行脚本文件
zsh_test.sh
,报错$ ./zsh_test.sh ./zsh_test.sh:3: = not found
恐怕您使用的测试命令有误。让我们看看为什么。
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
也可以。