如何验证程序是否存在,以返回错误并退出,或继续执行脚本?

看起来应该很容易,但这让我很为难。


当前回答

假设您已经遵循了安全壳做法:

set -eu -o pipefail
shopt -s failglob

./dummy --version 2>&1 >/dev/null

这假设命令可以以这样的方式调用,即它(几乎)什么都不做,比如报告其版本或显示帮助。

如果找不到伪命令,Bash将退出并返回以下错误。。。

./my-script: line 8: dummy: command not found

这比其他命令-v(和类似的)回答更有用,也更不冗长,因为错误消息是自动生成的,并且还包含相关的行号。

其他回答

为了模拟Bash的-P cmd类型,我们可以使用POSIX兼容的env-i type cmd 1>/dev/null 2>&1。

man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.

ls() { echo 'Hello, world!'; }

ls
type ls
env -i type ls

cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }

对于感兴趣的人来说,如果您希望检测已安装的库,则前面的答案中的方法都不起作用。我想你要么要检查路径(可能是头文件之类的),要么就这样(如果你是基于Debian的发行版):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

从上面可以看到,查询中的“0”表示未安装包。这是“grep”的函数-“0”表示找到匹配项,“1”表示没有找到匹配项。

如果为要测试的<Command>设置了POSIX_BUILTINS选项,则命令-v可以正常工作,但如果没有,则可能会失败。(多年来,它一直对我有效,但我最近遇到了一个不起作用的地方。)

我发现以下内容更能防止故障:

test -x "$(which <command>)"

因为它测试三件事:路径、存在和执行权限。

我找不到一个可行的解决方案,但编辑了一下后,我想到了这个。这对我有用:

dpkg --get-selections | grep -q linux-headers-$(uname -r)

if [ $? -eq 1 ]; then
        apt-get install linux-headers-$(uname -r)
fi

答复

POSIX兼容:

command -v <the_command>

示例用法:

if ! command -v <the_command> &> /dev/null
then
    echo "<the_command> could not be found"
    exit
fi

对于Bash特定环境:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

解释

避免使用。它不仅是一个外部进程,你启动它的目的很小(这意味着像哈希、类型或命令这样的内置程序要便宜得多),你还可以依赖内置程序来实际执行你想要的操作,而外部命令的效果很容易因系统而异。

为什么要在意?

许多操作系统都有一个甚至不设置退出状态的函数,这意味着if-which foo在那里甚至不起作用,并且总是报告foo存在,即使它不存在(注意,有些POSIX shell似乎也会为hash设置)。许多操作系统都会做一些自定义和邪恶的事情,比如更改输出,甚至挂接到包管理器。

所以,不要使用哪个。请使用以下选项之一:

command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(次要的补充说明:有些人会建议2>&-与2>/dev/null相同,但更短–这是不正确的。2>&关闭FD 2,这会在程序尝试写入stderr时导致错误,这与成功写入stderr并丢弃输出非常不同(而且很危险!))

如果你的hash bang是/bin/sh,那么你应该关心POSIX怎么说。POSIX并没有很好地定义类型和哈希的退出代码,当命令不存在时,可以看到哈希成功退出(还没有看到这种类型)。POSIX很好地定义了命令的退出状态,所以使用它可能是最安全的。

如果您的脚本使用bash,POSIX规则就不再重要了,类型和散列都变得非常安全。type现在有一个-P来搜索PATH,而hash的副作用是命令的位置将被散列(以便下次使用时更快地查找),这通常是一件好事,因为为了实际使用它,您可能会检查它的存在。

作为一个简单的例子,这里有一个函数,如果gdate存在,则运行它,否则运行date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "$@"
    else
        date "$@"
    fi
}

具有完整功能集的替代方案

您可以使用常见的脚本来满足您的需要。

要检查是否安装了某些东西,可以执行以下操作:

checkBin <the_command> || errorMessage "This tool requires <the_command>. Install it please, and then run this tool again."