How to check if a program exists from a Bash script?
我将如何验证程序是否存在,以返回错误并退出或继续脚本的方式验证?
这看起来应该很容易,但一直困扰着我。
回答
POSIX兼容:
1 | command -v <the_command> |
对于
1 2 | hash <the_command> # For regular commands. Or... type <the_command> # To check built-ins and keywords |
解释
避开
为什么要关心?
- 许多操作系统都有一个甚至不设置退出状态的
which ,这意味着if which foo 甚至不会在那里工作,并且会始终报告foo 存在,即使它不存在(请注意,有些posix shell似乎也会为hash 做这件事)。 - 许多操作系统使
which 做定制和邪恶的事情,例如更改输出,甚至钩住包管理器。
所以,不要使用
1 2 3 | $ 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; } |
(小旁注:有些人会认为
如果您的hash bang是
如果脚本使用
作为一个简单的例子,这里有一个运行
1 2 3 4 5 6 7 | gnudate() { if hash gdate 2>/dev/null; then gdate"$@" else date"$@" fi } |
以下是检查
1 | [ -x"$(command -v foo)" ] |
例子:
1 2 3 4 | if ! [ -x"$(command -v git)" ]; then echo 'Error: git is not installed.' >&2 exit 1 fi |
需要执行检查,因为如果在
另外,请注意,如果在早期的
此外,如果要查找的命令已定义为别名,则此操作将失败。
我同意lhunath不鼓励使用
1 | $ command -v foo >/dev/null 2>&1 || { echo"I require foo but it's not installed. Aborting.">&2; exit 1; } |
命令
注:
我在.bashrc中定义了一个函数,它使这变得更容易。
1 2 3 | command_exists () { type"$1" &> /dev/null ; } |
下面是一个如何使用它的示例(来自我的
1 2 3 | if command_exists mvim ; then export VISUAL="mvim --nofork" fi |
这取决于您是否想知道它是否存在于
1 2 3 4 5 | if which programname >/dev/null; then echo exists else echo does not exist fi |
否则使用
1 2 3 4 5 | if [ -x /path/to/programname ]; then echo exists else echo does not exist fi |
在第一个例子中,重定向到
在@lhunath's和@gregv's答案的基础上,下面是那些希望轻松将支票放入
1 2 3 4 | exists() { command -v"$1">/dev/null 2>&1 } |
以下是如何使用它:
1 2 3 4 5 | if exists bash; then echo 'Bash exists!' else echo 'Your system does not have Bash' fi |
尝试使用:
1 | test -x filename |
或
1 | [ -x filename ] |
从条件表达式下的bash手册页:
1
2 -x file
True if file exists and is executable.
如@lhunath所建议的,在bash脚本中使用
1 2 3 4 | hash foo &> /dev/null if [ $? -eq 1 ]; then echo >&2"foo not found." fi |
这个脚本运行
我从来没有得到过以上的解决方案来处理我能接触到的盒子。首先,安装了类型(执行其他操作)。因此需要内置指令。这个命令对我有效:
1 | if [ `builtin type -p vim` ]; then echo"TRUE"; else echo"FALSE"; fi |
如果您检查程序是否存在,您可能稍后再运行它。为什么不先试着运行它呢?
1 2 3 4 5 | if foo --version >/dev/null 2>&1; then echo Found else echo Not found fi |
这是一个更值得信赖的检查程序是否运行,而不仅仅是查看路径目录和文件权限。
另外,您还可以从程序中获得一些有用的结果,比如它的版本。
当然,缺点是有些程序启动起来可能很重,有些程序没有
检查多个依赖项并向最终用户通知状态
1 2 3 4 5 6 7 8 | for cmd in latex pandoc; do printf '%-10s'"$cmd" if hash"$cmd" 2>/dev/null; then echo OK else echo missing fi done |
样品输出:
1 2 | latex OK pandoc missing |
将
如果可以,为什么不使用bash内置的呢?
1 | which programname |
…
1 | type -P programname |
以东十一〔22〕与西施、巴施、大施、亚施同工。
另请注意,
对于那些感兴趣的人,如果您希望检测已安装的库,上述方法都不起作用。我想您要么需要对路径进行物理检查(可能检查头文件等),要么像这样(如果您使用的是基于Debian的发行版):
1 2 3 4 5 | dpkg --status libdb-dev | grep -q not-installed if [ $? -eq 0 ]; then apt-get install libdb-dev fi |
从上面可以看到,查询中的"0"答案意味着没有安装包。这是"grep"的函数-"0"表示找到匹配项,"1"表示找不到匹配项。
我想说,没有便携式和100%可靠的方式,因为悬挂的
1 2 3 4 | alias john='ls --color' alias paul='george -F' alias george='ls -h' alias ringo=/ |
当然只有最后一个是有问题的(对林戈没有冒犯!)但从
为了拒绝像
请注意,这样的解决方案将无条件拒绝
1 | test() { command -v $1 | grep -qv alias } |
如果找到可执行文件,则返回0;如果找不到或不可执行,则返回1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | NAME which - locate a command SYNOPSIS which [-a] filename ... DESCRIPTION which returns the pathnames of the files which would be executed in the current environment, had its arguments been given as commands in a strictly POSIX-conformant shell. It does this by searching the PATH for executable files matching the names of the arguments. OPTIONS -a print all matching pathnames of each argument EXIT STATUS 0 if all specified commands are found and executable 1 if one or more specified commands is nonexistent or not exe- cutable 2 if an invalid option is specified |
很好的一点是,它可以找出运行环境中的可执行文件是否可用,从而节省了一些问题…
-亚当
如果没有可用的外部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # portable version of Bash's type -P cmd (without output on stdout) typep() { command -p env -i PATH="$PATH" sh -c ' export LC_ALL=C LANG=C cmd="$1" cmd="`type"$cmd" 2>/dev/null || { echo"error: command $cmd not found; exiting ..." 1>&2; exit 1; }`" [ $? != 0 ] && exit 1 case"$cmd" in *\ /*) exit 0;; *) printf"%s ""error: $cmd" 1>&2; exit 1;; esac ' _"$1" || exit 1 } # get your standard $PATH value #PATH="$(command -p getconf PATH)" typep ls typep builtin typep ls-temp |
至少在Mac OS X 10.6.8上,使用bash 4.2.24(2)
如果您想检查程序是否存在并且实际上是一个程序,而不是一个bash内置命令,那么
例如,有一个时间程序,它提供了比时间内置命令更多的功能。为了检查程序是否存在,我建议使用
1 2 3 4 5 6 7 8 9 10 11 12 | # first check if the time program exists timeProg=`which time` if ["$timeProg" ="" ] then echo"The time program does not exist on this system." exit 1 fi # invoke the time program $timeProg --quiet -o result.txt -f"%S %U + p" du -sk ~ echo"Total CPU time: `dc -f result.txt` seconds" rm result.txt |
这里有很多选择,但我很惊讶没有一个快速的单字,这是我在脚本开始时使用的:
[["$(command -v mvn)" ]] || { echo"mvn is not installed" 1>&2 ; exit 1; }
[["$(command -v java)" ]] || { echo"java is not installed" 1>&2 ; exit 1; }
这是基于这里选择的答案和另一个来源(和我玩一点)。
希望这对其他人有用。
我第二次使用"命令-v"。像这样:
1 2 3 4 5 | md=$(command -v mkdirhier) ; alias md=${md:=mkdir} # bash emacs="$(command -v emacs) -nw" || emacs=nano alias e=$emacs [[ -z $(command -v jed) ]] && alias jed=$emacs |
为了模仿bash的
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
如果你们不能让上面/下面的东西工作,把头发从背后拔出来,试着用
第一。它可以给你完全不同的输出。
1 2 3 4 | $ command -v ls alias ls='ls --color=auto' $ bash -c"command -v ls" /bin/ls |
第二。它根本不能给你任何输出。
1 2 3 4 5 | $ command -v nvm nvm $ bash -c"command -v nvm" $ bash -c"nvm --help" bash: nvm: command not found |
hash变量有一个陷阱:在命令行上,您可以键入
1 | one_folder/process |
执行进程。为此,一个文件夹的父文件夹必须位于$path中。但当您尝试散列此命令时,它将始终成功:
1 | hash one_folder/process; echo $? # will always output '0' |
它会根据位置判断程序是否存在
1 2 3 | if [ -x /usr/bin/yum ]; then echo This is Centos fi |
我的Debian服务器设置。当多个包包含相同的名称时,我遇到了一个问题。例如Apache2。所以这就是我的解决方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | function _apt_install() { apt-get install -y $1 > /dev/null } function _apt_install_norecommends() { apt-get install -y --no-install-recommends $1 > /dev/null } function _apt_available() { if [ `apt-cache search $1 | grep -o"$1" | uniq | wc -l` ="1" ]; then echo"Package is available : $1" PACKAGE_INSTALL="1" else echo"Package $1 is NOT available for install" echo "We can not continue without this package..." echo "Exitting now.." exit 0 fi } function _package_install { _apt_available $1 if ["${PACKAGE_INSTALL}" ="1" ]; then if ["$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" ="ii" ]; then echo "package is already_installed: $1" else echo "installing package : $1, please wait.." _apt_install $1 sleep 0.5 fi fi } function _package_install_no_recommends { _apt_available $1 if ["${PACKAGE_INSTALL}" ="1" ]; then if ["$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" ="ii" ]; then echo "package is already_installed: $1" else echo "installing package : $1, please wait.." _apt_install_norecommends $1 sleep 0.5 fi fi } |
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #!/bin/bash # Commands found in the hash table are checked for existence before being # executed and non-existence forces a normal PATH search. shopt -s checkhash function exists() { local mycomm=$1; shift || return 1 hash $mycomm 2>/dev/null || \ printf"\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist "; return 1; } readonly -f exists exists notacmd exists bash hash bash -c 'printf"Fin. "' |
结果
1 2 3 4 | ? [ABRT]: notacmd: command does not exist hits command 0 /usr/bin/bash Fin. |
我使用这个是因为它非常简单:
1 | if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo"not exists";fi |
或
1 2 3 4 | if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists else echo"not exists" fi |
它使用shell builtin和程序echo status到stdout,而不使用任何内容到stderr。另一方面,如果找不到命令,它只将status返回到stderr。
如果为
我发现以下内容更具防故障性:
1 | test -x $(which <command>) |
因为它测试三件事:路径、存在和执行权限。
1 2 3 4 5 6 | checkexists() { while [ -n"$1" ]; do [ -n"$(which"$1")" ] || echo"$1": command not found shift done } |
1 2 3 4 5 6 7 8 9 10 11 | GIT=/usr/bin/git # STORE THE RELATIVE PATH # GIT=$(which git) # USE THIS COMMAND TO SEARCH FOR THE RELATIVE PATH if [[ ! -e $GIT ]]; then # CHECK IF THE FILE EXISTS echo"PROGRAM DOES NOT EXIST." exit 1 # EXIT THE PROGRAM IF IT DOES NOT fi # DO SOMETHING ... exit 0 # EXIT THE PROGRAM IF IT DOES |
我必须检查是否安装了
1 2 3 | if ! builtin type -p git &>/dev/null; then sudo apt-get -y install git-core fi |
希望这能帮助别人!
我找不到一个有效的解决方案,但在编辑了一点之后,我想出了这个办法。这对我很有用:
1 2 3 4 5 | dpkg --get-selections | grep -q linux-headers-$(uname -r) if [ $? -eq 1 ]; then apt-get install linux-headers-$(uname -r) fi |
我试着用
如果找不到程序,将退出与
1 2 3 | #!/bin/bash set -e git --version >> /dev/null |
@lhunath的回答和解释真是太棒了。救了我的一天我把它延长了一点。无法控制自己分享它-希望它可能对某人有用。如果有人需要检查(一组)多个程序,这里是快速片段。
What it is doing? (1) Read array of programs. (2) Show message for
failed program. (3) Prompt user to continue (forcing loop) y/n options
for the validation of the rest of the programs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #!/bin/bash proginstalldir=/full/dir/path/of/installation progsbindir=$proginstalldir/bin echo -e" My install directory - $proginstalldir" echo -e"My binaries directory - $progsbindir" VerifyInstall () { clear myprogs=( program1 program2 program3 program4 program5 programn ); echo -e" Validation of my programs started...." for ((i=0; i<${#myprogs[@]}; i++)) ; do command -v $progsbindir/${myprogs[i]} >/dev/null && echo -e"Validating....\t${myprogs[i]}\tSUCCESSFUL" || { echo -e"Validating.... \t${myprogs[i]}\tFAILED">&2; while true; do printf"%s: " "ERROR.... Validation FAILED for ${myprogs[i]} !!!! Continue?"; read yn; case $yn in [Yy] ) echo -e"Please wait..." ; break;; [Nn]) echo -e" ################################# ## Validation Failed .. !! ## ################################# " ; exit 1; break;; *) echo -e" Please answer y or n then press Enter "; esac; done; >&2; }; done sleep 2 } VerifyInstall |