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

问:

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

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

答1:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

回答

POSIX 兼容:

command -v 

示例使用:

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

对于 Bash 特定环境:

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

解释

避免 which。它不仅是您为做很少的事情而启动的外部进程(意味着像 hash、type 或 command 这样的内置程序更便宜),您还可以依靠内置程序来实际做您想做的事情,而外部命令的效果很容易因系统而异。

为什么关心?

许多操作系统都有一个 which 甚至没有设置退出状态,这意味着 if which foo 甚至不会在那里工作,并且总是会报告 foo 存在,即使它不存在(请注意,一些 POSIX shell 似乎可以这也是哈希)。

许多操作系统都会做一些自定义和邪恶的事情,比如改变输出,甚至挂钩到包管理器。

所以,不要使用 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 时,这与成功写入并丢弃输出非常不同(而且很危险!))

如果您的哈希爆炸是 /bin/sh 那么您应该关心 POSIX 所说的内容。 type 和 hash 的退出代码在 POSIX 中定义得不是很好,并且当命令不存在时,可以看到 hash 成功退出(还没有看到 type 出现这种情况)。 command 的退出状态由 POSIX 很好地定义,因此使用起来可能是最安全的。

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

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

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

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

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

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

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

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

您介意解释一下 &>/dev/null 和 >&2 部分的用途吗?没有它们,这条线似乎也能正常工作。谢谢。

@Geert: &>/dev/null 部分隐藏了 'foo' 不存在时发出的消息 'type' 。 echo 上的 >&2 确保将错误消息发送到标准错误而不是标准输出;因为那是惯例。它们都出现在您的终端上,但标准错误绝对是错误消息和意外警告的首选输出。

对于那些不熟悉 bash 中的“高级”i/o 重定向的人: 1) 2>&- ("close output file descriptor 2", which is stderr) 与 2> /dev/null 具有相同的结果; 2) >&2 是 1>&2 的快捷方式,您可以将其识别为“将标准输出重定向到标准错误”。有关详细信息,请参阅 Advanced Bash Scripting Guide i/o redirection page。

答2:

与HuntsBot一起,探索全球自由职业机会–huntsbot.com

以下是检查命令是否存在于 $PATH 和 中的可移植方法:

[ -x "$(command -v foo)" ]

例子:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

需要执行可执行检查,因为如果在 $PATH 中找不到具有该名称的可执行文件,bash 将返回一个不可执行的文件。

另请注意,如果在 $PATH 的前面存在与可执行文件同名的不可执行文件,则 dash 返回前者,即使后者会被执行。这是一个错误,违反了 POSIX 标准。 [Bug report] [Standard]

此外,如果您要查找的命令已被定义为别名,这将失败。

答3:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

我同意 lhunath 不鼓励使用 which,他的解决方案对于 Bash 用户来说是完全有效的。但是,为了更便携,应使用 command -v 代替:

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

命令 command 符合 POSIX。有关其规范,请参见此处:command - execute a simple command

注意:type 符合 POSIX,但 type -P 不符合。

与上面相同 - exit 1; 如果从那里调用,则会杀死一个 xterm。

这不适用于标准 sh:you &> 不是有效的重定向指令。

@jyavenard:这个问题被标记为 bash,因此更简洁的 bash 特定重定向符号 &>/dev/null。但是,我同意你的看法,真正重要的是可移植性,我已经相应地编辑了我的答案,现在使用标准 sh 重定向 >/dev/null 2>&1。

为了进一步改进这个答案,我会做两件事:1:使用“&>”来简化它,就像乔希的回答一样。 2:将 { } 分成额外的一行,在回显之前放置一个制表符,以提高可读性

内置词和保留字失败:例如,用词 then 试试这个。如果您需要可执行文件存在于 $PATH 中,请参阅 this answer。

答4:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

这取决于您是否想知道它是否存在于 $PATH 变量的某个目录中,或者您是否知道它的绝对位置。如果您想知道它是否在 $PATH 变量中,请使用

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

否则使用

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

在第一个示例中重定向到 /dev/null/ 会抑制 which 程序的输出。

由于我的评论中概述的原因,您真的不应该使用“which”。

答5:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

我在我的 .bashrc 中定义了一个函数,使这更容易。

command_exists () {
    type "$1" &> /dev/null ;
}

这是一个如何使用它的示例(来自我的 .bash_profile。)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
fi

&> 有什么作用?

&> redirects both stdout and stderr 在一起。

&> 在您的 Bash 版本中可能不可用。 Marcello 的代码应该可以正常工作;它做同样的事情。

内置词和保留字失败:例如,用词 then 试试这个。如果您需要可执行文件存在于 $PATH 中,请参阅 this answer。

答6:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

扩展@lhunath 和@GregV 的答案,下面是希望轻松将该检查放入 if 语句中的人的代码:

exists()
{
  command -v "$1" >/dev/null 2>&1
}

以下是如何使用它:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi

学习和改进的意愿必须得到奖励。 +1 这是干净和简单的。我唯一可以补充的是,即使对于别名,command 也会成功,这可能有点违反直觉。在交互式 shell 中检查是否存在与将其移动到脚本时会给出不同的结果。

我刚刚测试并使用 shopt -u expand_aliases 忽略/隐藏别名(如另一个答案中提到的 alias ls='ls -F')和 shopt -s expand_aliases 通过 command -v 解决它们。因此,也许它应该在检查之前设置并在之后取消设置,尽管如果您不明确捕获并返回命令调用的输出,它可能会影响函数返回值。

为什么这在 if exists conda; then 上不起作用,即使安装了 anaconda 并在终端中输入 conda 时返回:usage: conda [-h] [-V] command...? (请注意,我已验证您的答案适用于 Ubuntu 20 操作系统上的 if exists bash; then。)

@在 which conda

@Palec 它不仅仅是别名。 command -v 还显示内置函数、函数甚至关键字,并为它们返回 0!

答7:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

尝试使用:

test -x filename

或者

[ -x filename ]

在 Conditional Expressions 下的 Bash 手册页中:

-x 文件 如果文件存在且可执行,则为真。

这意味着您需要已经知道应用程序的完整路径。

OP 没有指定他是否要检查特定实例或任何可执行实例……我以阅读的方式回答了它。

答8:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

要在 Bash 脚本中使用 hash、as @lhunath suggests:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

此脚本运行 hash,然后检查最近命令的退出代码(存储在 $? 中的值)是否等于 1。如果 hash 未找到 foo,则退出代码将为 1。如果存在 foo,则退出代码将为 0。

&> /dev/null 从 hash 重定向 standard error 和 standard output,使其不会出现在屏幕上,并且 echo >&2 将消息写入标准错误。

为什么不只是 if hash foo &> /dev/null; then ... ?

如果设置了 set -e,则永远不会运行 if... 部分。

答9:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

这里有很多选择,但我很惊讶没有快速的单线。这是我在脚本开始时使用的:

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

这是基于此处选择的答案和另一个来源。

[[ 和命令替换在这里没有用。如果未找到 mvn,只需 command -v mvn || exit 将 exit;但是有了这个固定,这基本上只是重复了接受的答案。

答10:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

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

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

test -x "$(which )"

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

不工作。 test -x $(which ls) 与 test -x $(which sudo) 一样返回 0,即使 ls 已安装且可运行,并且 sudo 甚至未安装在我正在运行的 docker 容器中。

@algal 我认为您需要使用引号,所以test -x "$(which )"

@algal 也许 ls 是别名?如果命令有参数,我认为它不会起作用。

我不能保证这个答案,但我也推荐引用。 $ test -x $(which absent_cmd) 返回 test: too many arguments,而 test -x "$(which absent_cmd)" 已正确解析并导致退出代码 1。

@acorello,test: too many arguments 建议 which absent_cmd 返回多字错误消息。可能存在与消息同名的可执行文件。 😁 以这种方式使用 which 是个坏主意。

答11:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

如果您检查程序是否存在,您可能会在以后运行它。为什么不首先尝试运行它?

if foo --version >/dev/null 2>&1; then
    echo Found
else
    echo Not found
fi

与仅查看 PATH 目录和文件权限相比,检查程序是否运行更可靠。

另外,您可以从您的程序中获得一些有用的结果,例如它的版本。

当然,缺点是有些程序启动起来很繁重,有些程序没有立即(并成功)退出的 --version 选项。

原文链接:https://www.huntsbot.com/qa/nbAy/how-can-i-check-if-a-program-exists-from-a-bash-script?lang=zh_CN&from=csdn

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值