我正在阅读为 Bourne shell 编写的 Maven 包装器的源代码。我遇到过这些行:
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
# snip
expr
当与arg1
and一起使用时arg2
, a 与正则表达式:
匹配。通常,结果是匹配字符的数量,例如:arg1
arg2
$ expr foobar : foo
3
但是,当使用捕获括号 (\(
和\)
) 时,它返回第一个捕获括号的内容:
$ expr foobar : '\(foo\)'
foo
到目前为止,一切都很好。
如果我在我的机器上评估上面引用的源中的表达式,我会得到:
$ javaExecutable=$(which javac)
$ expr "$javaExecutable" : '\([^ ]*\)'
/usr/bin/javac
对于不存在的可执行文件:
$ nonExistingExecutable=$(which sjdkfjkdsjfs)
$ expr "$nonExistingExecutable" : '\([^ ]*\)'
这意味着对于不存在的可执行文件,输出是带有换行符的空字符串。
which javac
在源代码中让我困惑的是( arg1
to )的输出如何expr
返回字符串no
?
是否有某个版本在找不到可执行文件时which
不返回任何内容,而是返回?no
如果不是,这个语句总是评估为真,那就很奇怪了。
传统上,
which
是一个 csh 脚本,它打印一条错误消息no foo in /usr/bin:/bin
,并返回一个成功状态。(至少有一个通用版本,可能还有其他行为不同的版本。)来自 FreeBSD 1.0 的示例(是的,那是古老的):(这个经典的实现也因加载用户的 而臭名昭著
.cshrc
,这可能会更改PATH
,从而导致输出错误。)现代系统通常有不同的 实现
which
,用 C 或 sh 编写,并遵循处理错误条件的现代标准:没有输出到 stdout 和非零退出状态。从技术上讲,这是检查输出中的第一个单词(或者更确切地说,第一个非空格字符序列)。并且有些
which
实现并没有保持沉默:zsh
写入which foo
标准foo not found
输出which foo
写入which: no foo in (<contents of $PATH>)
stderr我不知道以 开头的打印输出是哪个特定变体
no
,但 GNUwhich
可能受到它的启发或者成为它的灵感。