默认测试表达式在 zsh 与 bash 中的行为不同 - 为什么?
Default test expression behaves different in zsh vs bash - why?
这是一个简单的测试用例脚本,当我从命令行使用 运行 和 $ source test_script.sh
时,它在 zsh 和 bash 中的行为不同。我不一定知道如果我的 shebang 明确指出我想要 bash 到 运行 我的脚本而不是 which
命令是内置的事实,为什么会有区别zsh 和 bash 中的一个程序。 (仅供参考 - shebang 目录是我的 bash 程序所在的位置,它可能与你的不同——我使用自制软件安装了一个新版本)
#!/usr/local/bin/bash
if [ "$(which ls)" ]; then
echo "ls command found"
else
echo "ls command not found"
fi
if [ "$(which foo)" ]; then
echo "foo command found"
else
echo "foo command not found"
我 运行 这个脚本来自 zsh source ./test-script.sh
和 Bash.
zsh 中的输出:
ls command found
foo command found
bash中的输出:
ls command found
foo command not found
我的理解是 test
或 [ ]
(它们是同一事物)的默认值将字符串评估为 true,如果它不是 empty/null。举例说明:
zsh:
$ which foo
foo not found
bash:
$ which foo
$
此外,如果我在 zsh 中重定向标准错误,例如:
$ which foo 2> /dev/null
foo not found
zsh 似乎仍然将 foo not found
发送到标准输出,这就是为什么(我猜)我的测试用例在 zshell 下都通过了;因为在这两种情况下 "$(which xxx)"
return 的扩展都是一个字符串(例如 /some/directory
和 foo not found
(zsh 总是 return 一个字符串?)。
最后,如果我删除双引号(例如 $(which xxx)
),zsh 会给我一个错误。这是输出:
ls command found
test_scritp.sh:27: condition expected not:
我猜 zsh 希望我使用 [ ! "$(which xxx)" ]
。我不明白为什么?在 bash 中 运行ning 时,它从来没有给出该错误(这不应该在 bash 中 运行 吗?!)。
为什么我的脚本没有使用 bash?为什么像这样微不足道的事情不起作用?我了解如何使用 -e
选项使其在两种情况下都能正常工作,但我只是想了解为什么会发生这一切。它让我发疯。
这里有两个不同的问题。
首先,正确使用的命令是type
,而不是which
。正如您所注意到的,命令 which
是一个 zsh
内置命令,而在 Bash 中,它将执行您系统上恰好存在的任何 which
命令。有许多具有不同行为的变体,这就是为什么 POSIX 选择引入替代品而不是尝试为 which
指定特定行为的原因 - 那么就会有更多可能的行为,而且没有办法轻松根除所有其他遗留行为。 (一个早期的常见问题是 which
命令会检查 csh
环境,即使您实际上使用了不同的 shell。)
其次,检查命令的字符串输出是一个严重的反模式,因为字符串在语言环境("not found" vs. "nicht gefunden" vs. "ei löytynyt" vs. 等)和程序版本之间不同-- 正确的解决方案是检查命令的退出代码。
if type ls >/dev/null 2>&1; then
echo "ls command found"
else
echo "ls command not found"
fi
if type foo >/dev/null 2>&1; then
echo "foo command found"
else
echo "foo command not found"
fi
(一个相关的反模式是显式地检查 $?
。很少需要这样做,因为它是由 shell 的流程控制语句自然而透明地完成的,比如if
和 while
。)
关于引用,shell 对未引用的值执行空白标记化和通配符扩展,因此如果 $string
是 command not found
,表达式
[ $string ]
不带引号的值计算为
[ command not found ]
在 shell 中看起来像字符串 "command" 后跟一些在语法上无效的废话。
最后,正如我们在聊天会话(从评论链接)中发现的那样,OP 对 source
的确切含义感到困惑,并最终 运行 一个 Bash 脚本而是一个单独的过程。 (./test-script
而不是 source ./test-script
)。作为记录,当您 source
一个文件时,您会导致当前 shell 读取并执行它;在此设置中,脚本的 shebang 行只是一条注释,并被 shell.
完全忽略
这是一个简单的测试用例脚本,当我从命令行使用 运行 和 $ source test_script.sh
时,它在 zsh 和 bash 中的行为不同。我不一定知道如果我的 shebang 明确指出我想要 bash 到 运行 我的脚本而不是 which
命令是内置的事实,为什么会有区别zsh 和 bash 中的一个程序。 (仅供参考 - shebang 目录是我的 bash 程序所在的位置,它可能与你的不同——我使用自制软件安装了一个新版本)
#!/usr/local/bin/bash
if [ "$(which ls)" ]; then
echo "ls command found"
else
echo "ls command not found"
fi
if [ "$(which foo)" ]; then
echo "foo command found"
else
echo "foo command not found"
我 运行 这个脚本来自 zsh source ./test-script.sh
和 Bash.
zsh 中的输出:
ls command found
foo command found
bash中的输出:
ls command found
foo command not found
我的理解是 test
或 [ ]
(它们是同一事物)的默认值将字符串评估为 true,如果它不是 empty/null。举例说明:
zsh:
$ which foo
foo not found
bash:
$ which foo
$
此外,如果我在 zsh 中重定向标准错误,例如:
$ which foo 2> /dev/null
foo not found
zsh 似乎仍然将 foo not found
发送到标准输出,这就是为什么(我猜)我的测试用例在 zshell 下都通过了;因为在这两种情况下 "$(which xxx)"
return 的扩展都是一个字符串(例如 /some/directory
和 foo not found
(zsh 总是 return 一个字符串?)。
最后,如果我删除双引号(例如 $(which xxx)
),zsh 会给我一个错误。这是输出:
ls command found
test_scritp.sh:27: condition expected not:
我猜 zsh 希望我使用 [ ! "$(which xxx)" ]
。我不明白为什么?在 bash 中 运行ning 时,它从来没有给出该错误(这不应该在 bash 中 运行 吗?!)。
为什么我的脚本没有使用 bash?为什么像这样微不足道的事情不起作用?我了解如何使用 -e
选项使其在两种情况下都能正常工作,但我只是想了解为什么会发生这一切。它让我发疯。
这里有两个不同的问题。
首先,正确使用的命令是type
,而不是which
。正如您所注意到的,命令 which
是一个 zsh
内置命令,而在 Bash 中,它将执行您系统上恰好存在的任何 which
命令。有许多具有不同行为的变体,这就是为什么 POSIX 选择引入替代品而不是尝试为 which
指定特定行为的原因 - 那么就会有更多可能的行为,而且没有办法轻松根除所有其他遗留行为。 (一个早期的常见问题是 which
命令会检查 csh
环境,即使您实际上使用了不同的 shell。)
其次,检查命令的字符串输出是一个严重的反模式,因为字符串在语言环境("not found" vs. "nicht gefunden" vs. "ei löytynyt" vs. 等)和程序版本之间不同-- 正确的解决方案是检查命令的退出代码。
if type ls >/dev/null 2>&1; then
echo "ls command found"
else
echo "ls command not found"
fi
if type foo >/dev/null 2>&1; then
echo "foo command found"
else
echo "foo command not found"
fi
(一个相关的反模式是显式地检查 $?
。很少需要这样做,因为它是由 shell 的流程控制语句自然而透明地完成的,比如if
和 while
。)
关于引用,shell 对未引用的值执行空白标记化和通配符扩展,因此如果 $string
是 command not found
,表达式
[ $string ]
不带引号的值计算为
[ command not found ]
在 shell 中看起来像字符串 "command" 后跟一些在语法上无效的废话。
最后,正如我们在聊天会话(从评论链接)中发现的那样,OP 对 source
的确切含义感到困惑,并最终 运行 一个 Bash 脚本而是一个单独的过程。 (./test-script
而不是 source ./test-script
)。作为记录,当您 source
一个文件时,您会导致当前 shell 读取并执行它;在此设置中,脚本的 shebang 行只是一条注释,并被 shell.