当找不到可执行文件时,“which”的任何实现是否输出“no”?

当找不到可执行文件时,“which”的任何实现是否输出“no”?

我正在读为 Bourne shell 编写的 Maven 包装器的源代码。我遇到过这些行:

if [ -z "$JAVA_HOME" ]; then
    javaExecutable="$(which javac)"
    if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
# snip

exprarg1当与and一起使用时,arg2a与正则:表达式匹配。通常,结果是匹配字符的数量,例如:arg1arg2

$ expr foobar : foo
3

但是,当使用捕获括号 (\(\)) 时,它返回第一个捕获括号的内容:

$ expr foobar : '\(foo\)'
foo

到目前为止,一切都很好。

如果我在我的机器上评估上面引用的源中的表达式,我会得到:

$ javaExecutable=$(which javac)
$ expr "$javaExecutable" : '\([^ ]*\)'
/usr/bin/javac

对于不存在的可执行文件:

$ nonExistingExecutable=$(which sjdkfjkdsjfs)
$ expr "$nonExistingExecutable" : '\([^ ]*\)'

这意味着对于不存在的可执行文件,输出是带有换行符的空字符串。

which javac在源代码中让我困惑的是( arg1to )的输出如何expr返回字符串no

是否有某个版本在找不到可执行文件时不which返回任何内容,而是返回?no

如果不是,这个语句总是评估为真,那就很奇怪了。

答案1

传统上,which是一个 csh 脚本,它打印一条错误消息no foo in /usr/bin:/bin,并返回一个成功状态。 (至少有一个常见版本,可能还有其他行为不同的版本。)示例从 FreeBSD 1.0 开始(是的,那是古老的):

    if ( ! $?found ) then
    echo no $arg in $path
    endif

(这个经典的实现也因加载用户的 而臭名昭著.cshrc,这可能会更改PATH,从而导致输出错误。)

现代系统通常有不同的 实现which,用 C 或 sh 编写,并遵循处理错误条件的现代标准:没有输出到 stdout 和非零退出状态。

答案2

从技术上讲,这是检查是否第一个字(或者更确切地说,第一个非空格字符序列)在输出中。并且有些which实现并没有保持沉默:

  • zsh写入which foo标准foo not found输出
  • GNUwhich foo写入which: no foo in (<contents of $PATH>)stderr
  • 我没有这些可以测试,但是一堆旧 Unix 的联机帮助页上有这样的措辞:“如果在路径中找不到具有参数名称的可执行文件,则打印诊断信息”(尽管我希望诊断信息能够打印到 stderr)。

我不知道以 开头的打印输出是哪个特定变体no,但 GNUwhich可能受到它的启发或者成为它的灵感。

相关内容