bash - 如何从 Bash 脚本检查程序是否存在?

标签 bash

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

看起来应该很容易,但它一直困扰着我。

最佳答案

回答

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

说明

避免which。它不仅是您启动的外部进程,执行的操作很少(这意味着像 hashtypecommand 这样的内置函数要便宜得多),您还可以依赖内置函数来实际执行您想要的操作,而外部命令的效果很容易因系统而异。

为什么要关心?

  • 许多操作系统都有一个 which,它甚至不设置退出状态,这意味着 if which foo 甚至无法在那里工作,并且会始终报告 foo 存在,即使它不存在(请注意,某些 POSIX shell 似乎也会对 hash 执行此操作)。
  • 许多操作系统都会制作which来执行自定义和邪恶的操作,例如更改输出,甚至 Hook 到包管理器。

所以,不要使用which。而是使用以下之一:

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 时导致错误,这与成功写入并丢弃输出非常不同(而且很危险!))

如果你的 hash bang 是 /bin/sh 那么你应该关心 POSIX 所说的内容。 POSIX 没有很好地定义 typehash 的退出代码,当命令不存在时,hash 会成功退出(还没有在 type 中看到这一点)。 command 的退出状态由 POSIX 明确定义,因此它可能是最安全的使用方式。

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

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

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

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

您可以使用scripts-common以满足您的需求。

要检查是否已安装某些内容,您可以执行以下操作:

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

关于bash - 如何从 Bash 脚本检查程序是否存在?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21936498/

相关文章:

bash使用bc计算,并对 float 进行四舍五入

linux - 如何在 bash 中循环获取退出状态

regex - IF 语句在 RHEL 6 中不起作用(在 RHEL 5 中起作用)

linux - Shell:如何将多个文件移动到一个目录并用不同的名称压缩该目录?

linux - 自动检测文件更改并通过 S3 同步

linux - 在 linux 中获取硬盘的可用空间

bash - 如何使用 "ls"而不是整个目录内容获取目录条目的日期?

python - 移动包含旧文件的目录的简单方法

bash - 将 csv 转换为文本

python - 如何在 Subprocess.run 命令中使用 for 循环