如何验证程序是否存在,以某种方式返回错误并退出,或继续使用脚本?
看起来应该很容易,但它一直在困扰我。
答案 0 :(得分:2624)
POSIX兼容:
command -v <the_command>
对于bash
特定环境:
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
避免使用which
。它不仅是一个外部过程,你只是做了很少的事情(意味着像hash
,type
或command
这样的内置更便宜),你也可以依靠内置来实现做你想做的事,而外部命令的影响很容易因系统而异。
为什么要关心?
which
甚至没有设置退出状态,这意味着if which foo
甚至无法在那里工作,并且总是< / strong>报告foo
存在,即使它不存在(请注意,某些POSIX shell似乎也为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所说的内容。 POSIX没有很好地定义type
和hash
的退出代码,并且当命令不存在时,hash
被视为成功退出(没有看到{ {1}}。 type
的退出状态由POSIX很好地定义,因此可能是最安全的。
如果您的脚本使用command
,则POSIX规则不再重要,bash
和type
变得非常安全。 hash
现在只有type
来搜索-P
而PATH
有副作用,该命令的位置将被哈希(以便下次使用时更快查找) ,这通常是一件好事,因为你可能会检查它的存在,以便实际使用它。
举一个简单的例子,这是一个运行hash
的函数(如果存在),否则gdate
:
date
答案 1 :(得分:401)
以下是检查$PATH
中是否存在命令以及是否可执行的可移植方式:
[ -x "$(command -v foo)" ]
示例:
if ! [ -x "$(command -v git)" ]; then
echo 'Error: git is not installed.' >&2
exit 1
fi
需要执行可执行文件检查,因为如果在$PATH
中找不到具有该名称的可执行文件,bash将返回非可执行文件。
另请注意,如果$PATH
中先前存在与可执行文件同名的非可执行文件,则破折号将返回前者,即使后者将被执行。这是一个错误,违反了POSIX标准。 [Bug report] [Standard]
此外,如果您要查找的命令已被定义为别名,则此操作将失败。
答案 2 :(得分:196)
我同意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标准,请参阅此处了解其规范:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
注意:type
符合POSIX标准,但type -P
不符合。
答案 3 :(得分:84)
我的.bashrc中定义了一个函数,使这更容易。
command_exists () {
type "$1" &> /dev/null ;
}
以下是一个如何使用它的示例(来自我的.bash_profile
。)
if command_exists mvim ; then
export VISUAL="mvim --nofork"
fi
答案 4 :(得分:73)
这取决于您是否想知道它是否存在于$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
程序的输出。
答案 5 :(得分:31)
扩展@ lhunath&#39;和@ 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
答案 6 :(得分:21)
尝试使用:
test -x filename
或
[ -x filename ]
来自Conditional Expressions下的bash联机帮助页:
-x file True if file exists and is executable.
答案 7 :(得分:15)
在bash脚本中使用hash
,@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
的标准错误和标准输出,以便它不会出现在屏幕上,而echo >&2
会将消息写入标准错误。
答案 8 :(得分:10)
我从来没有让上述解决方案在我可以访问的盒子上工作。例如,已安装类型(执行更多操作)。所以需要内置指令。这个命令对我有用:
if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi
答案 9 :(得分:8)
如果检查程序是否存在,您可能会在以后运行它。为什么不尝试首先运行它?
if foo --version >/dev/null 2>&1; then
echo Found
else
echo Not found
fi
这是一个更值得信赖的检查程序运行,而不仅仅是查看PATH目录和文件权限。
另外,您可以从程序中获得一些有用的结果,例如它的版本。
当然缺点是某些程序可能很重要,有些程序没有--version
选项可以立即(并成功)退出。
答案 10 :(得分:7)
检查多个依赖项并告知最终用户状态
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方式:
How to align the columns of a space separated table in Bash?
答案 11 :(得分:6)
如果可以,为什么不使用Bash builtins?
which programname
...
type -P programname
答案 12 :(得分:6)
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
。
答案 13 :(得分:4)
对于那些感兴趣的人,如果您希望检测已安装的库,则上述方法都不起作用。我想你会留下物理检查路径(可能是头文件等),或类似的东西(如果你在基于Debian的发行版上):
dpkg --status libdb-dev | grep -q not-installed
if [ $? -eq 0 ]; then
apt-get install libdb-dev
fi
从上面可以看出,查询中的“0”答案表示未安装包。这是“grep”的函数 - “0”表示找到匹配,“1”表示未找到匹配。
答案 14 :(得分:3)
which
命令可能很有用。 man which
如果找到可执行文件,则返回0;如果找不到可执行文件,则返回1:
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
关于它的好处是,它确定可执行文件是否在运行的环境中可用 - 节省了一些问题......
- 亚当
答案 15 :(得分:3)
我认为由于悬空alias
而没有便携式和100%可靠的方式。例如:
alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/
当然只有最后一个是有问题的(对Ringo没有冒犯!)但从alias
的角度来看,所有这些都是有效的command -v
。
为了拒绝像ringo
这样的悬空,我们必须解析shell内置alias
命令的输出并递归到它们中(command -v
并不优于{{ 1}}这里。)没有便携式解决方案,即使是特定于Bash的解决方案也相当繁琐。
请注意,此类解决方案将无条件拒绝alias
alias ls='ls -F'
答案 16 :(得分:2)
如果没有可用的外部type
命令(理所当然here),我们可以使用POSIX兼容env -i sh -c 'type cmd 1>/dev/null 2>&1'
:
# 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\n" "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)command -v ls
与移动的/bin/ls-temp
不匹配。
答案 17 :(得分:1)
仅zsh
,但对zsh
脚本编写非常有用(例如,在编写完成脚本时):
zsh/parameter
模块允许访问内部 commands
哈希表等。来自man zshmodules
:
THE ZSH/PARAMETER MODULE
The zsh/parameter module gives access to some of the internal hash ta‐
bles used by the shell by defining some special parameters.
[...]
commands
This array gives access to the command hash table. The keys are
the names of external commands, the values are the pathnames of
the files that would be executed when the command would be in‐
voked. Setting a key in this array defines a new entry in this
table in the same way as with the hash builtin. Unsetting a key
as in `unset "commands[foo]"' removes the entry for the given
key from the command hash table.
虽然是可加载的模块,但好像是默认加载的,只要zsh
不和--emulate
一起使用。
示例:
martin@martin ~ % echo $commands[zsh]
/usr/bin/zsh
要快速检查某个命令是否可用,只需检查哈希中是否存在该键:
if (( ${+commands[zsh]} ))
then
echo "zsh is available"
fi
请注意,散列将包含 $PATH
文件夹中的任何文件,无论它们是否可执行。可以肯定的是,您必须为此花费 stat
调用:
if (( ${+commands[zsh]} )) && [[ -x $commands[zsh] ]]
then
echo "zsh is available"
fi
答案 18 :(得分:1)
哈希变体有一个缺陷:在命令行上,您可以输入
one_folder/process
执行流程。为此,one_folder的父文件夹必须位于 $ PATH 中。但是当你尝试散列这个命令时,它总会成功:
hash one_folder/process; echo $? # will always output '0'
答案 19 :(得分:1)
为了模仿Bash的type -P cmd
,我们可以使用符合POSIX的env -i type cmd 1>/dev/null 2>&1
。
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; }
答案 20 :(得分:1)
我的debian服务器设置。 当多个包包含相同的名称时,我遇到了问题。 例如apache2。 所以这是我的解决方案。
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
}
答案 21 :(得分:1)
这可能更简单,只是:
#!/usr/bin/env bash
set -x
# if local program 'foo' returns 1 (doesn't exist) then...
if ! type -P foo; then
echo 'crap, no foo'
else
echo 'sweet, we have foo!'
fi
将foo
更改为vi
,以激发其他条件。
答案 22 :(得分:1)
我希望回答相同的问题,但要在Makefile中运行。
install:
@if [[ ! -x "$(shell command -v ghead)" ]]; then \
echo 'ghead does not exist. Please install it.'; \
exit -1; \
fi
答案 23 :(得分:1)
如果你想检查程序是否存在并且确实是程序,而不是bash内置命令,那么command
,type
和{{1不适合测试,因为它们都为内置命令返回0退出状态。
例如,时间程序提供的功能多于时间内置命令。要检查程序是否存在,我建议使用hash
,如下例所示:
which
答案 24 :(得分:1)
我第二次使用“command -v”。例如。像这样:
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
答案 25 :(得分:1)
这里有很多选择,但是我没有一个快速的单行令我感到惊讶,这是我在脚本开始时使用的:
[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }
这是基于此处选择的答案和另一个来源(和我一起玩的情况)。
希望这对其他人会很方便。
答案 26 :(得分:1)
它将根据位置告诉程序是否存在
if [ -x /usr/bin/yum ]; then
echo This is Centos
fi
答案 27 :(得分:0)
脚本
#!/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\n"; return 1;
}
readonly -f exists
exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'
结果
✘ [ABRT]: notacmd: command does not exist
hits command
0 /usr/bin/bash
Fin.
答案 28 :(得分:0)
最新答案,但这就是我最终要做的事情。
我只是检查我执行的命令是否返回错误代码。如果返回0,则表示已安装程序。此外,您可以使用它来检查脚本的输出。以这个脚本为例。
foo.sh
#!/bin/bash
echo "hello world"
exit 1 # throw some error code
示例:
# outputs something bad... and exits
bash foo.sh $? -eq 0 || echo "something bad happened. not installed" ; exit 1
# does NOT outputs nothing nor exits because dotnet is installed on my machine
dotnet --version $? -eq 0 || echo "something bad happened. not installed" ; exit 1
基本上,所有这些操作都是检查命令运行的退出代码。 即使命令退出代码不为0,此问题上最被接受的答案也将返回true。
答案 29 :(得分:0)
假设您已经遵循安全的shell做法:https://sipb.mit.edu/doc/safe-shell/
set -eu -o pipefail
shopt -s failglob
./dummy --version 2>&1 >/dev/null
这假定可以以几乎不执行任何操作的方式调用该命令,例如报告其版本或显示帮助。
如果未找到dummy
命令,bash将退出,并显示以下错误...
./my-script: line 8: dummy: command not found
这是比其他command -v
(和类似的答案)更有用且更简洁的方法,因为错误消息是自动生成的,并且还包含相关的行号。
答案 30 :(得分:0)
嘿:
if [[ `command --help` ]]; then
echo "This command exists"
else
echo "This command does not exist";
fi
在if检查中放置一个工作开关,例如“-help” 或“-v” : if [[{command --help
]] ;然后
答案 31 :(得分:0)
<command>
设置了POSIX_BUILTINS选项以进行测试,则命令-v可以正常工作,但是如果没有,则该命令可能会失败。 (它对我有用了很多年,但最近遇到了一个不起作用的地方)。
我发现以下内容可以更好地防止故障:
test -x $(which <command>)
因为它测试了三件事:路径,存在和执行权限。
答案 32 :(得分:0)
我使用它是因为它很容易:
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi
或
if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi
如果找不到命令,它会使用shell内置程序并将程序回显状态编程到stdout而对stderr没有任何内容,它只对stderr发回状态。
答案 33 :(得分:-1)
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
答案 34 :(得分:-1)
我必须检查是否已安装git
作为部署CI服务器的一部分。我的最终bash脚本如下(Ubuntu服务器):
if ! builtin type -p git &>/dev/null; then
sudo apt-get -y install git-core
fi
希望这有助于其他人!
答案 35 :(得分:-1)
checkexists() {
while [ -n "$1" ]; do
[ -n "$(which "$1")" ] || echo "$1": command not found
shift
done
}
答案 36 :(得分:-2)
@lhunath的出色回答和解释。拯救了我的一天。 我扩展了一点。无法控制自己分享它-希望它对某人有用。如果有人需要检查(一个或多个)程序,这是快速片段。
它在做什么? (1)读取程序数组。 (2)显示消息 失败的程序。 (3)提示用户继续(强制循环)y / n选项 验证其余程序。
#!/bin/bash
proginstalldir=/full/dir/path/of/installation
progsbindir=$proginstalldir/bin
echo -e "\nMy install directory - $proginstalldir"
echo -e "My binaries directory - $progsbindir"
VerifyInstall () {
clear
myprogs=( program1 program2 program3 program4 program5 programn );
echo -e "\nValidation 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 "\n\n#################################\n## Validation Failed .. !! ##\n#################################\n\n" ; exit 1; break;;
*) echo -e "\nPlease answer y or n then press Enter\n"; esac; done; >&2; }; done
sleep 2
}
VerifyInstall
答案 37 :(得分:-2)
我只想用--version
或--help
和check if the command succeeded or failed来调用程序
如果找不到程序,则与set -e
脚本一起使用的脚本将退出,并且您将收到一条有意义的错误消息:
#!/bin/bash
set -e
git --version >> /dev/null
答案 38 :(得分:-2)
我无法让其中一个解决方案起作用,但经过一段时间的编辑后,我想出了这个。哪个适合我:
dpkg --get-selections | grep -q linux-headers-$(uname -r)
if [ $? -eq 1 ]; then
apt-get install linux-headers-$(uname -r)
fi