如何验证程序是否存在,以返回错误并退出或继续脚本的方式?
看起来应该很容易,但它一直在困扰我.
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所说的. 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
}
对于那些不熟悉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.net/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
.
另请注意,如果先前存在与可执行文件同名的非可执行文件,则$PATH
dash会返回前者,即使后者将被执行.这是一个错误,违反了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
不可用ash
和dash
.