当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何从Bash脚本检查程序是否存在?》经验,为你挑选了12个好方法。

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

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



1> lhunath..:

回答

POSIX兼容:

command -v 

对于bash特定环境:

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

说明

避免which.它不仅是一个外部进程,你只是做了很少的事情(意味着内置hash,type或者command更便宜),你也可以依靠内置实际做你想要的,而外部命令的效果可以很容易地从系统到系统.

为何关心?

许多操作系统有which甚至不设置退出状态,这意味着if which foo甚至不会在那里工作,并会始终报告foo存在,即使它不(注意,有些POSIX炮弹似乎对这样做hash太).

许多操作系统会which做自定义和恶意的事情,比如更改输出甚至挂钩到包管理器.

所以,不要使用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所说的. typehash的退出代码不是非常好由POSIX定义的,并且hash被认为是当所述命令不存在成功地退出(未与看到这种type尚未). commandPOSIX很好地定义了退出状态,因此最安全的可能是最安全的.

如果你的脚本使用bash虽然POSIX规则真的不重要了两者typehash成为完全安全的使用. type现在有一个-P只搜索PATH并且hash有副作用的命令的位置将被哈希(为了下次你使用它时更快的查找),这通常是一件好事,因为你可能检查它的存在,以便实际使用它.

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

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


对于那些不熟悉bash中"高级"i/o重定向的人:1)`2>& - `*("关闭输出文件描述符2",这是stderr)*与`2>/dev/null具有相同的结果; 2)`>&2`是`1>&2`的快捷方式,您可以将其识别为"将stdout重定向到stderr".有关详细信息,请参阅[Advanced Bash Scripting Guide i/o重定向页面](http://tldp.org/LDP/abs/html/io-redirection.html).
@mikewaters`2>& - `**不是**与'2>/dev/null`相同.前者关闭文件描述符,而后者只是将其重定向到`/ dev/null`.您可能看不到错误,因为程序试图通过stderr通知您stderr已关闭.
@Geert:当'foo'不存在时,&>/dev/null部分隐藏消息'type'.echo上的>&2确保将错误消息发送到标准错误而不是标准输出; 因为这是惯例.它们都出现在您的终端上,但标准错误肯定是错误消息和意外警告的首选输出.
@mikewaters ABS看起来相当先进,描述了广泛的bash和非bash CLI功能,但它在很多方面都非常疏忽,并没有遵循良好的做法.在评论中我没有足够的空间来写一篇文章; 但我可以粘贴一些随机的BAD代码示例:`read元素; do .. done <<< $(echo $ {ArrayVar [*]})`,`for $ in $(fgrep -l $ ORIGINAL*.txt)`,`ls -l"$ directory"| sed 1d`,{{for a seq $ BEGIN $ END`}},...许多人试图联系作者并提出改进建议,但这并不是维基,而且请求已被置若罔闻.
-P标志在'sh'中不起作用,例如http://stackoverflow.com/questions/2608688/check-if-a-program-exists-in-bash
如果没有foo,你的`exit 1`会杀死xterm.非常感谢你!
在类似POSIX.1-2004的shell中,保证`type`,`command -v`或`hash`都不存在.请参阅http://stackoverflow.com/q/34572700/1175080和http://stackoverflow.com/q/34572886/1175080.
如果`command`最安全地使用POSIX约束,即使我使用bash,为什么还要使用`type`或`hash`?
@ b.long花括号用于组合`echo`和`exit`命令,以便它们都作为`command`的退出代码运行.如果省略花括号,只有`echo`命令将有条件地运行,并且`exit`命令将始终运行,无论命令是否存在.请参阅http://mywiki.wooledge.org/BashGuide/TestsAndConditionals#Grouping_Statements
值得注意的是,如果`foo`是shell别名,内置函数或函数,那么`foo`(如果存在的话)很可能会失败,而上面的POSIX替代品会产生成功.因此,例如``foo && foo`比`命令-v foo>/dev/null 2>&1 && foo`更接近普通`命令foo`.
我对所有这些方法进行了效率比较:https://www.topbug.ne​​t/blog/2016/10/11/speed-test-check-the-existence-of-a-command-in-bash-and- zsh的/
@mikewaters我不认为链接到LDP的ABS是非常负责任的.bash专家普遍认为它对bash学生有不良影响.相反,我建议以下内容来了解​​bash中的I/O:http://mywiki.wooledge.org/BashSheet#Streams
@CMCDragonkai在这种情况下,你通常根本不会进行测试而只是运行命令.您可以在命令前加上!(和空格)否定其退出状态,或者你可以用`&&'替换`||`.参见`man bash`或http://mywiki.wooledge.org/BashGuide/TestsAndConditionals#Control_Operators_.28.26.26_and_.7C.7C.29
@lhunah这个页面的标题问:"如何检查程序是否存在于bash脚本中?",唯一真正的方法是测试文件是否存在,例如:[ - e/bin/ls ],所以:你需要文件的正确位置而不是抽象词(ls).更多的额外选择.此外,如果将ls程序更改为/ bin/ls-temp之类的命令,则"命令-v ls"仍然报告相同,而"which"确实报告"null"(空).
替代品不是黑客.替代方案100%良好且便携,"哪个"没有获得单一优势,并且存在许多缺点.`which`实际上并不告诉你在运行命令时你的shell会做什么.`哪个'是谎言.只有你的shell可以告诉你命令名在那个shell中解析的内容.只有内置的shell才能提供您需要的信息.不要再是传播错误信息的毒药.
@mkobit Bash的文档在`man bash`中.可以使用`help command`获得简短的摘要

2> nyuszika7h..:

以下是检查命令是否存在$PATH 可执行的可移植方法:

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

例:

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

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

另请注意,如果先前存在与可执行文件同名的非可执行文件,则$PATHdash会返回前者,即使后者将被执行.这是一个错误,违反了POSIX标准.[ 错误报告 ] [ 标准 ]

此外,如果您要查找的命令已被定义为别名,则此操作将失败.


@einpoklum是的,这是必要的.实际上,即使这种解决方案也可能在一个边缘情况下破裂.谢谢你引起我的注意.执行命令时,dash,bash和zsh都跳过`$ PATH`中的非可执行文件.但是,`command -v`的行为非常不一致.在dash中,它返回`$ PATH`中的第一个匹配文件,无论它是否可执行.在bash中,它返回`$ PATH`中的第一个可执行匹配,但如果没有,则它可以返回一个不可执行的文件.在zsh中,它永远不会返回非可执行文件.
即使对于非可执行文件,`command -v`也会产生路径吗?也就是说,-x真的有必要吗?
@einpoklum`-x`测试文件是否可执行,这就是问题所在.
据我所知,"破折号"是这三种中唯一符合非POSIX标准的; `[-x"$(命令-v COMMANDNAME)"]`将在另外两个中起作用.看起来这个bug已经被报道但尚未得到任何回复:https://bugs.debian.org/cgi-bin/bugreport.cgi?video = 874264
@KenSharp:但这似乎是多余的,因为`command`本身会测试它是可执行的 - 不是吗?
@ nyuszika7h重要的是要注意,如果您要检查的命令是*不是*可执行文件,而是shell函数或内置函数,此解决方案将会中断.它应该专门用于检查可执行文件.

3> GregV..:

我同意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 command符合POSIX标准,请参阅此处了解其规范:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

注意:type符合POSIX标准,但type -P不是.


@jyavenard:问题标记为_bash_,因此更简洁的bash特定重定向符号`&>/dev/null`.但是,我同意你的意见,真正重要的是可移植性,我已经相应地编辑了我的答案,现在使用标准的sh重定向`>/dev/null 2>&1`.
与上面相同 - `退出1;`如果从那里调用,则杀死xterm.

4> Josh Strater..:

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

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

这是一个如何使用它的例子(从我的.bash_profile.)

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


`&>`[重定向stdout和stderr](http://www.gnu.org/software/bash/manual/bash.html#Redirecting-Standard-Output-and-Standard-Error).
无法使用内置指令和保留字:例如,尝试使用`then`一词。如果您要求可执行文件存在于$ PATH中,请参见[this answer](/sf/ask/17360801/)。

5> dreamlax..:

这取决于您是否想知道它是否存在于$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".

6> Romário..:

扩展@ 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这很干净简单.我唯一可以添加的是`命令`甚至对于别名也能成功,这可能有点违反直觉.检查交互式shell中是否存在会在将其移动到脚本时产生不同的结果.
@Palec:你是对的,它非常笨拙.我把它清理了一下,我希望现在看起来更合适.
几乎尽可能笨拙.阅读[man bash`中的退出状态](https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html)并了解如何使用它.它将使您的代码更简单,更优雅.括号不是`if`语法的一部分,它们只是命令`test`的简写.`if`检查命令是否成功(退出状态为0).

7> dmckee..:

尝试使用:

test -x filename

要么

[ -x filename ]

从条件表达式下的bash手册页:

 -x file
          True if file exists and is executable.


这意味着您需要已经知道应用程序的完整路径.
OP没有说明他是否要检查特定实例或任何可执行实例......我按照我的方式回答它.

8> dcharles..:

要使用hash,如@lhunath表明,在bash脚本:

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

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

&> /dev/null重定向标准错误和标准输出,hash以便它不会出现在屏幕上并将echo >&2消息写入标准错误.


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

9> Magnus..:

我从来没有得到上述解决方案在我可以访问的盒子上工作.例如,已安装类型(执行更多操作).所以需要内置指令.这个命令对我有用:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi


括号不是`if`语法的一部分,只需使用`if builtin type -p vim; 然后......` 反引号是非常古老和不赞成的语法,在所有现代系统上,甚至`sh`都支持`$()`.

10> Ciro Santill..:

检查多个依赖项并将状态通知给最终用户

for cmd in latex pandoc; do
  printf '%-10s' "$cmd"
  if hash "$cmd" 2>/dev/null; then
    echo OK
  else
    echo missing
  fi
done

样本输出:

latex     OK
pandoc    missing

调整10到最大命令长度.不是自动的,因为我没有看到非冗长的POSIX方法: 如何在Bash中对齐空格分隔表的列?



11> 0xF..:

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

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

这是一个更值得信赖的检查程序运行,而不仅仅是查看PATH目录和文件权限.

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

当然缺点是某些程序可能很重要,有些程序没有--version立即(并成功)退出的选项.



12> blueyed..:

hash foo 2>/dev/null:适用于zsh,bash,dash和ash.

type -p foo:它似乎与zsh,bash和ash(busybox)一起使用,但不是破折号(它解释-p为参数).

command -v foo:适用于zsh,bash,dash,但不适用于ash(busybox)(-ash: command: not found).

还要注意的是builtin不可用ashdash.

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有