检查一个程序是否存在于Bash脚本中。

时间:2022-01-27 11:38:42

How would I validate that a program exists, in a way that will either return an error and exit, or continue with the script?

我如何验证一个程序的存在,它会返回错误和退出,或者继续执行脚本?

It seems like it should be easy, but it's been stumping me.

看起来应该很容易,但我却被难住了。

29 个解决方案

#1


2136  

Answer

POSIX compatible:

POSIX兼容:

command -v <the_command>

For bash specific environments:

bash特定环境:

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

Explanation

Avoid which. Not only is it an external process you're launching for doing very little (meaning builtins like hash, type or command are way cheaper), you can also rely on the builtins to actually do what you want, while the effects of external commands can easily vary from system to system.

避免它。它不仅是您启动的一个外部进程,而且您还可以依赖于构建程序来实际执行您想要的操作,而外部命令的效果可以很容易地随系统的不同而变化。

Why care?

为什么在乎吗?

  • Many operating systems have a which that doesn't even set an exit status, meaning the if which foo won't even work there and will always report that foo exists, even if it doesn't (note that some POSIX shells appear to do this for hash too).
  • 许多操作系统都有一个甚至没有设置退出状态的操作系统,这意味着如果foo在那里不工作,并且总是报告foo存在,即使它不存在(注意一些POSIX shell也会这样做)。
  • Many operating systems make which do custom and evil stuff like change the output or even hook into the package manager.
  • 许多操作系统都做一些定制的和邪恶的事情,比如改变输出,甚至把它们连接到包管理器中。

So, don't use which. Instead use one of these:

所以,不要使用。取而代之的是:

$ 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; }

(Minor side-note: some will suggest 2>&- is the same 2>/dev/null but shorter – this is untrue. 2>&- closes FD 2 which causes an error in the program when it tries to write to stderr, which is very different from successfully writing to it and discarding the output (and dangerous!))

(小侧边注:有些人会建议2>&-是相同的2>/dev/null但更短-这是不真实的。>&-关闭FD 2,当它试图写入stderr时,会导致程序错误,这与成功写入和丢弃输出(和危险!)非常不同。

If your hash bang is /bin/sh then you should care about what POSIX says. type and hash's exit codes aren't terribly well defined by POSIX, and hash is seen to exit successfully when the command doesn't exist (haven't seen this with type yet). command's exit status is well defined by POSIX, so that one is probably the safest to use.

如果你的hash bang是/bin/sh,那么你应该关心POSIX说的是什么。类型和散列的退出代码不是很好地由POSIX定义,并且当命令不存在时,可以看到散列成功退出(还没有看到这种类型)。命令的退出状态由POSIX很好地定义,因此使用它可能是最安全的。

If your script uses bash though, POSIX rules don't really matter anymore and both type and hash become perfectly safe to use. type now has a -P to search just the PATH and hash has the side-effect that the command's location will be hashed (for faster lookup next time you use it), which is usually a good thing since you probably check for its existence in order to actually use it.

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

As a simple example, here's a function that runs gdate if it exists, otherwise date:

作为一个简单的例子,这里有一个运行gdate的函数,如果它存在,则为:

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

#2


185  

I agree with lhunath to discourage use of which, and his solution is perfectly valid for BASH users. However, to be more portable, command -v shall be used instead:

我同意lhunath不鼓励使用它,他的解决方案对于BASH用户是完全有效的。然而,为了更便携,命令-v应使用代替:

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

Command command is POSIX compliant, see here for its specification: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

命令命令是POSIX兼容的,请参阅这里的规范:http://pubs.opengroup.org/onlinepubs/9699919799/ use /command.html。

Note: type is POSIX compliant, but type -P is not.

注意:type是POSIX兼容的,但是type -P不是。

#3


175  

The following is a portable way to check whether a command exists in $PATH and is executable:

以下是一种可移植的方法来检查命令是否存在于$PATH中,并且是可执行的:

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

Example:

例子:

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

The executable check is needed because bash returns a non-executable file if no executable file with that name is found in $PATH.

由于bash返回一个非可执行文件,如果没有在$PATH中找到该名称的可执行文件,则需要执行检查。

Also note that if a non-executable file with the same name as the executable exists earlier in $PATH, dash returns the former, even though the latter would be executed. This is a bug and is in violation of the POSIX standard. [Bug report] [Standard]

还要注意,如果一个与可执行文件同名的非可执行文件在$PATH中存在,那么dash将返回前者,即使后者将被执行。这是一个错误,并且违反了POSIX标准。(错误报告][标准]

In addition, this will fail if the command you are looking for has been defined as an alias.

此外,如果您正在寻找的命令被定义为别名,那么这将失败。

#4


78  

I have a function defined in my .bashrc that makes this easier.

我在。bashrc中定义了一个函数,使它更简单。

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

Here's an example of how it's used (from my .bash_profile.)

下面是如何使用它的示例(来自my .bash_profile)。

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

#5


64  

It depends whether you want to know whether it exists in one of the directories in the $PATH variable or whether you know the absolute location of it. If you want to know if it is in the $PATH variable, use

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

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

otherwise use

否则使用

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

The redirection to /dev/null/ in the first example suppresses the output of the which program.

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

#6


25  

Expanding on @lhunath's and @GregV's answers, here's the code for the people who want to easily put that check inside an if statement:

扩展@lhunath和@GregV的答案,下面是那些想要在if语句中轻松放入check的人的代码:

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

Here's how to use it:

下面是如何使用它:

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

#7


19  

Try using:

尝试使用:

test -x filename

or

[ -x filename ]

From the bash manpage under Conditional Expressions:

根据条件表达式的bash manpage:

 -x file
          True if file exists and is executable.

#8


16  

To use hash, as @lhunath suggests, in a bash script:

正如@lhunath在bash脚本中所建议的,使用散列:

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

This script runs hash and then checks if the exit code of the most recent command, the value stored in $?, is equal to 1. If hash doesn't find foo, the exit code will be 1. If foo is present, the exit code will be 0.

该脚本运行散列,然后检查最近的命令的退出代码是否为$?等于1。如果哈希没有找到foo,那么退出代码将是1。如果foo存在,退出代码将为0。

&> /dev/null redirects standard error and standard output from hash so that it doesn't appear onscreen and echo >&2 writes the message to standard error.

&> /dev/null重定向标准错误和标准输出,使其不会出现在屏幕上,而echo >&2将消息写入标准错误。

#9


7  

I never did get the above solutions to work on the box I have access to. For one, type has been installed (doing what more does). So the builtin directive is needed. This command works for me:

我从来没有得到过上面的解决方案。首先,已经安装了类型(做更多的事情)。所以我们需要构建指令。这个命令对我有效:

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

#10


7  

If you check for program existence, you are probably going to run it later anyway. Why not try to run it in the first place?

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

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

It's a more trustworthy check that the program runs than merely looking at PATH directories and file permissions.

比起仅仅查看路径目录和文件权限,程序运行起来更值得信任。

Plus you can get some useful result from your program, such as its version.

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

Of course the drawbacks are that some programs can be heavy to start and some don't have a --version option to immediately (and successfully) exit.

当然,缺点是有些程序可能很重启动,有些程序没有——版本选项立即(并成功地)退出。

#11


4  

For those interested, none of the methodologies above work if you wish to detect an installed library. I imagine you are left either with physically checking the path (potentially for header files and such), or something like this (if you are on a Debian-based distro):

对于那些感兴趣的,如果您希望检测一个已安装的库,以上方法都不适用。我想您要么是在物理上检查路径(可能是头文件之类的),要么是这样(如果您在基于debian的发行版中):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

As you can see from the above, a "0" answer from the query means the package is not installed. This is a function of "grep" - a "0" means a match was found, a "1" means no match was found.

正如您从上面看到的,来自查询的“0”答案意味着该包没有安装。这是“grep”的函数——“0”表示匹配,“1”表示没有匹配。

#12


4  

Why not use Bash builtins if you can?

如果可以,为什么不使用Bash builtins呢?

which programname

...

type -P programname

#13


4  

hash foo 2>/dev/null: works with zsh, bash, dash and ash.

hash foo 2>/dev/null:与zsh、bash、dash和ash一起工作。

type -p foo: it appears to work with zsh, bash and ash (busybox), but not dash (it interprets -p as an argument).

类型-p foo:它似乎与zsh、bash和ash (busybox)一起工作,但不是dash(它将-p解释为一个参数)。

command -v foo: works with zsh, bash, dash, but not ash (busybox) (-ash: command: not found).

命令- vfoo:使用zsh、bash、dash,但不使用ash (busybox) (-ash:命令:未找到)。

Also note that builtin is not available with ash and dash.

也要注意,内置的灰和破折号是不可用的。

#14


4  

Check for multiple dependencies and inform status to end users

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

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

Sample output:

样例输出:

latex     OK
pandoc    missing

Adjust the 10 to the maximum command length. Not automatic because I don't see a non verbose way to do it.

将10调整到最大命令长度。不是自动的,因为我没有看到非冗长的方法。

#15


2  

The which command might be useful. man which

该命令可能有用。男人的

It returns 0 if the executable is found, 1 if it's not found or not executable:

如果找到可执行文件,它将返回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

Nice thing about which is that it figures out if the executable is available in the environment that which is run in - saves a few problems...

很好的一点是,如果可执行文件在运行的环境中是可用的,那么它可以解决一些问题……

-Adam

亚当

#16


2  

I'd say there's no portable and 100% reliable way due to dangling aliases. For example:

我想说的是,由于悬挂的别名,没有可移植的和100%可靠的方法。例如:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

Of course only the last one is problematic (no offence to Ringo!) But all of them are valid aliases from the point of view of command -v.

当然,只有最后一个是有问题的(对Ringo没有冒犯!)但从命令-v的角度来看,它们都是有效的别名。

In order to reject dangling ones like ringo, we have to parse the output of the shell built-in alias command and recurse into them (command -v is no superior to alias here.) There's no portable solution for it, and even a Bash-specific solution is rather tedious.

为了拒绝像ringo这样的悬空操作,我们必须解析shell内置的别名命令的输出,并将其递归到它们(命令-v在这里没有更好的别名)。它没有可移植的解决方案,甚至一个特定于bash的解决方案也相当乏味。

Note that solution like this will unconditionally reject alias ls='ls -F'

注意,像这样的解决方案将无条件地拒绝别名ls='ls -F'

test() { command -v $1 | grep -qv alias }

#17


1  

To mimic Bash's type -P cmd we can use POSIX compliant env -i type cmd 1>/dev/null 2>&1.

为了模拟Bash的类型- pcmd,我们可以使用符合POSIX的env -i型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; }

#18


1  

The hash-variant has one pitfall: On the command line you can for example type in

hash-变体有一个陷阱:在命令行上,可以输入示例。

one_folder/process

to have process executed. For this the parent folder of one_folder must be in $PATH. But when you try to hash this command, it will always succeed:

流程执行。为此,one_folder的父文件夹必须位于$PATH中。但是,当您试图哈希这个命令时,它总是会成功:

hash one_folder/process; echo $? # will always output '0'

#19


1  

I second the use of "command -v". E.g. like this:

我第二次使用“命令-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

#20


1  

If there is no external type command available (as taken for granted here), we can use POSIX compliant env -i sh -c 'type cmd 1>/dev/null 2>&1':

如果没有可用的外部类型命令(如这里所认为的),我们可以使用符合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

At least on Mac OS X 10.6.8 using Bash 4.2.24(2) command -v ls does not match a moved /bin/ls-temp.

至少在Mac OS X 10.6.8上使用Bash 4.2.24(2)命令- vls不匹配移动/bin/ls- temp。

#21


1  

my setup for a debian server. i had a the problem when multiple packages contains the same name. for example apache2. so this was my solution.

我的debian服务器的设置。当多个包包含相同的名称时,我遇到了一个问题。例如输入。这就是我的解。

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
}

#22


1  

If you guys can't get the things above/below to work and pulling hair out of your back, try to run the same command using bash -c. Just look at this somnambular delirium, this is what really happening when you run $(sub-command):

如果你不能把上面/下面的东西从你的背后拉出来,用bash -c来运行同样的命令。看看这张图片,这就是当你运行$(子命令)时发生的事情:

First. It can give you completely different output.

第一。它可以给你完全不同的输出。

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Second. It can give you no output at all.

第二。它可以给你任何输出。

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found

#23


0  

checkexists() {
    while [ -n "$1" ]; do
        [ -n "$(which "$1")" ] || echo "$1": command not found
        shift
    done
}

#24


0  

I use this because it's very easy:

我用这个是因为它很简单:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

or

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

It uses shell builtin and program echo status to stdout and nothing to stderr by the other hand if a command is not found, it echos status only to stderr.

它使用shell builtin和程序echo status为stdout,而如果没有找到命令,它就不会对stderr产生任何影响,它只对stderr进行回波状态。

#25


0  

In case you want to check if a program exists and is really a program, not a bash built-in command, then command, type and hash are not appropriate for testing as they all return 0 exit status for built-in commands.

如果您想检查一个程序是否存在,是否真的是一个程序,而不是一个bash内置命令,那么命令、类型和散列都不适合测试,因为它们都返回了内置命令的0退出状态。

For example, there is the time program which offers more features than the time built-in command. To check if the program exists, I would suggest using which as in the following example:

例如,有一个时间程序,它提供了比内置命令更多的特性。为了检查程序是否存在,我建议使用以下示例:

# 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

#26


0  

Script

脚本

#!/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"'

Result

结果

✘ [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.

#27


-1  

I had to check if git was installed as part of deploying our CI server. My final bash script was as follows (Ubuntu server):

我必须检查git是否安装作为部署CI服务器的一部分。我的最后一个bash脚本如下(Ubuntu服务器):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

Hope this help someone else!

希望这能帮助别人!

#28


-1  

I couldn't get one of the solutions to work, but after editing it a little I came up with this. Which works for me:

我无法找到一个解决方案,但在编辑后,我想出了这个。这工作对我来说:

dpkg --get-selections | grep -q linux-headers-$(uname -r)

if [ $? -eq 1 ]; then
        apt-get install linux-headers-$(uname -r)
fi

#29


-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

#1


2136  

Answer

POSIX compatible:

POSIX兼容:

command -v <the_command>

For bash specific environments:

bash特定环境:

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

Explanation

Avoid which. Not only is it an external process you're launching for doing very little (meaning builtins like hash, type or command are way cheaper), you can also rely on the builtins to actually do what you want, while the effects of external commands can easily vary from system to system.

避免它。它不仅是您启动的一个外部进程,而且您还可以依赖于构建程序来实际执行您想要的操作,而外部命令的效果可以很容易地随系统的不同而变化。

Why care?

为什么在乎吗?

  • Many operating systems have a which that doesn't even set an exit status, meaning the if which foo won't even work there and will always report that foo exists, even if it doesn't (note that some POSIX shells appear to do this for hash too).
  • 许多操作系统都有一个甚至没有设置退出状态的操作系统,这意味着如果foo在那里不工作,并且总是报告foo存在,即使它不存在(注意一些POSIX shell也会这样做)。
  • Many operating systems make which do custom and evil stuff like change the output or even hook into the package manager.
  • 许多操作系统都做一些定制的和邪恶的事情,比如改变输出,甚至把它们连接到包管理器中。

So, don't use which. Instead use one of these:

所以,不要使用。取而代之的是:

$ 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; }

(Minor side-note: some will suggest 2>&- is the same 2>/dev/null but shorter – this is untrue. 2>&- closes FD 2 which causes an error in the program when it tries to write to stderr, which is very different from successfully writing to it and discarding the output (and dangerous!))

(小侧边注:有些人会建议2>&-是相同的2>/dev/null但更短-这是不真实的。>&-关闭FD 2,当它试图写入stderr时,会导致程序错误,这与成功写入和丢弃输出(和危险!)非常不同。

If your hash bang is /bin/sh then you should care about what POSIX says. type and hash's exit codes aren't terribly well defined by POSIX, and hash is seen to exit successfully when the command doesn't exist (haven't seen this with type yet). command's exit status is well defined by POSIX, so that one is probably the safest to use.

如果你的hash bang是/bin/sh,那么你应该关心POSIX说的是什么。类型和散列的退出代码不是很好地由POSIX定义,并且当命令不存在时,可以看到散列成功退出(还没有看到这种类型)。命令的退出状态由POSIX很好地定义,因此使用它可能是最安全的。

If your script uses bash though, POSIX rules don't really matter anymore and both type and hash become perfectly safe to use. type now has a -P to search just the PATH and hash has the side-effect that the command's location will be hashed (for faster lookup next time you use it), which is usually a good thing since you probably check for its existence in order to actually use it.

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

As a simple example, here's a function that runs gdate if it exists, otherwise date:

作为一个简单的例子,这里有一个运行gdate的函数,如果它存在,则为:

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

#2


185  

I agree with lhunath to discourage use of which, and his solution is perfectly valid for BASH users. However, to be more portable, command -v shall be used instead:

我同意lhunath不鼓励使用它,他的解决方案对于BASH用户是完全有效的。然而,为了更便携,命令-v应使用代替:

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

Command command is POSIX compliant, see here for its specification: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

命令命令是POSIX兼容的,请参阅这里的规范:http://pubs.opengroup.org/onlinepubs/9699919799/ use /command.html。

Note: type is POSIX compliant, but type -P is not.

注意:type是POSIX兼容的,但是type -P不是。

#3


175  

The following is a portable way to check whether a command exists in $PATH and is executable:

以下是一种可移植的方法来检查命令是否存在于$PATH中,并且是可执行的:

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

Example:

例子:

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

The executable check is needed because bash returns a non-executable file if no executable file with that name is found in $PATH.

由于bash返回一个非可执行文件,如果没有在$PATH中找到该名称的可执行文件,则需要执行检查。

Also note that if a non-executable file with the same name as the executable exists earlier in $PATH, dash returns the former, even though the latter would be executed. This is a bug and is in violation of the POSIX standard. [Bug report] [Standard]

还要注意,如果一个与可执行文件同名的非可执行文件在$PATH中存在,那么dash将返回前者,即使后者将被执行。这是一个错误,并且违反了POSIX标准。(错误报告][标准]

In addition, this will fail if the command you are looking for has been defined as an alias.

此外,如果您正在寻找的命令被定义为别名,那么这将失败。

#4


78  

I have a function defined in my .bashrc that makes this easier.

我在。bashrc中定义了一个函数,使它更简单。

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

Here's an example of how it's used (from my .bash_profile.)

下面是如何使用它的示例(来自my .bash_profile)。

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

#5


64  

It depends whether you want to know whether it exists in one of the directories in the $PATH variable or whether you know the absolute location of it. If you want to know if it is in the $PATH variable, use

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

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

otherwise use

否则使用

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

The redirection to /dev/null/ in the first example suppresses the output of the which program.

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

#6


25  

Expanding on @lhunath's and @GregV's answers, here's the code for the people who want to easily put that check inside an if statement:

扩展@lhunath和@GregV的答案,下面是那些想要在if语句中轻松放入check的人的代码:

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

Here's how to use it:

下面是如何使用它:

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

#7


19  

Try using:

尝试使用:

test -x filename

or

[ -x filename ]

From the bash manpage under Conditional Expressions:

根据条件表达式的bash manpage:

 -x file
          True if file exists and is executable.

#8


16  

To use hash, as @lhunath suggests, in a bash script:

正如@lhunath在bash脚本中所建议的,使用散列:

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

This script runs hash and then checks if the exit code of the most recent command, the value stored in $?, is equal to 1. If hash doesn't find foo, the exit code will be 1. If foo is present, the exit code will be 0.

该脚本运行散列,然后检查最近的命令的退出代码是否为$?等于1。如果哈希没有找到foo,那么退出代码将是1。如果foo存在,退出代码将为0。

&> /dev/null redirects standard error and standard output from hash so that it doesn't appear onscreen and echo >&2 writes the message to standard error.

&> /dev/null重定向标准错误和标准输出,使其不会出现在屏幕上,而echo >&2将消息写入标准错误。

#9


7  

I never did get the above solutions to work on the box I have access to. For one, type has been installed (doing what more does). So the builtin directive is needed. This command works for me:

我从来没有得到过上面的解决方案。首先,已经安装了类型(做更多的事情)。所以我们需要构建指令。这个命令对我有效:

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

#10


7  

If you check for program existence, you are probably going to run it later anyway. Why not try to run it in the first place?

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

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

It's a more trustworthy check that the program runs than merely looking at PATH directories and file permissions.

比起仅仅查看路径目录和文件权限,程序运行起来更值得信任。

Plus you can get some useful result from your program, such as its version.

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

Of course the drawbacks are that some programs can be heavy to start and some don't have a --version option to immediately (and successfully) exit.

当然,缺点是有些程序可能很重启动,有些程序没有——版本选项立即(并成功地)退出。

#11


4  

For those interested, none of the methodologies above work if you wish to detect an installed library. I imagine you are left either with physically checking the path (potentially for header files and such), or something like this (if you are on a Debian-based distro):

对于那些感兴趣的,如果您希望检测一个已安装的库,以上方法都不适用。我想您要么是在物理上检查路径(可能是头文件之类的),要么是这样(如果您在基于debian的发行版中):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

As you can see from the above, a "0" answer from the query means the package is not installed. This is a function of "grep" - a "0" means a match was found, a "1" means no match was found.

正如您从上面看到的,来自查询的“0”答案意味着该包没有安装。这是“grep”的函数——“0”表示匹配,“1”表示没有匹配。

#12


4  

Why not use Bash builtins if you can?

如果可以,为什么不使用Bash builtins呢?

which programname

...

type -P programname

#13


4  

hash foo 2>/dev/null: works with zsh, bash, dash and ash.

hash foo 2>/dev/null:与zsh、bash、dash和ash一起工作。

type -p foo: it appears to work with zsh, bash and ash (busybox), but not dash (it interprets -p as an argument).

类型-p foo:它似乎与zsh、bash和ash (busybox)一起工作,但不是dash(它将-p解释为一个参数)。

command -v foo: works with zsh, bash, dash, but not ash (busybox) (-ash: command: not found).

命令- vfoo:使用zsh、bash、dash,但不使用ash (busybox) (-ash:命令:未找到)。

Also note that builtin is not available with ash and dash.

也要注意,内置的灰和破折号是不可用的。

#14


4  

Check for multiple dependencies and inform status to end users

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

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

Sample output:

样例输出:

latex     OK
pandoc    missing

Adjust the 10 to the maximum command length. Not automatic because I don't see a non verbose way to do it.

将10调整到最大命令长度。不是自动的,因为我没有看到非冗长的方法。

#15


2  

The which command might be useful. man which

该命令可能有用。男人的

It returns 0 if the executable is found, 1 if it's not found or not executable:

如果找到可执行文件,它将返回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

Nice thing about which is that it figures out if the executable is available in the environment that which is run in - saves a few problems...

很好的一点是,如果可执行文件在运行的环境中是可用的,那么它可以解决一些问题……

-Adam

亚当

#16


2  

I'd say there's no portable and 100% reliable way due to dangling aliases. For example:

我想说的是,由于悬挂的别名,没有可移植的和100%可靠的方法。例如:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

Of course only the last one is problematic (no offence to Ringo!) But all of them are valid aliases from the point of view of command -v.

当然,只有最后一个是有问题的(对Ringo没有冒犯!)但从命令-v的角度来看,它们都是有效的别名。

In order to reject dangling ones like ringo, we have to parse the output of the shell built-in alias command and recurse into them (command -v is no superior to alias here.) There's no portable solution for it, and even a Bash-specific solution is rather tedious.

为了拒绝像ringo这样的悬空操作,我们必须解析shell内置的别名命令的输出,并将其递归到它们(命令-v在这里没有更好的别名)。它没有可移植的解决方案,甚至一个特定于bash的解决方案也相当乏味。

Note that solution like this will unconditionally reject alias ls='ls -F'

注意,像这样的解决方案将无条件地拒绝别名ls='ls -F'

test() { command -v $1 | grep -qv alias }

#17


1  

To mimic Bash's type -P cmd we can use POSIX compliant env -i type cmd 1>/dev/null 2>&1.

为了模拟Bash的类型- pcmd,我们可以使用符合POSIX的env -i型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; }

#18


1  

The hash-variant has one pitfall: On the command line you can for example type in

hash-变体有一个陷阱:在命令行上,可以输入示例。

one_folder/process

to have process executed. For this the parent folder of one_folder must be in $PATH. But when you try to hash this command, it will always succeed:

流程执行。为此,one_folder的父文件夹必须位于$PATH中。但是,当您试图哈希这个命令时,它总是会成功:

hash one_folder/process; echo $? # will always output '0'

#19


1  

I second the use of "command -v". E.g. like this:

我第二次使用“命令-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

#20


1  

If there is no external type command available (as taken for granted here), we can use POSIX compliant env -i sh -c 'type cmd 1>/dev/null 2>&1':

如果没有可用的外部类型命令(如这里所认为的),我们可以使用符合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

At least on Mac OS X 10.6.8 using Bash 4.2.24(2) command -v ls does not match a moved /bin/ls-temp.

至少在Mac OS X 10.6.8上使用Bash 4.2.24(2)命令- vls不匹配移动/bin/ls- temp。

#21


1  

my setup for a debian server. i had a the problem when multiple packages contains the same name. for example apache2. so this was my solution.

我的debian服务器的设置。当多个包包含相同的名称时,我遇到了一个问题。例如输入。这就是我的解。

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
}

#22


1  

If you guys can't get the things above/below to work and pulling hair out of your back, try to run the same command using bash -c. Just look at this somnambular delirium, this is what really happening when you run $(sub-command):

如果你不能把上面/下面的东西从你的背后拉出来,用bash -c来运行同样的命令。看看这张图片,这就是当你运行$(子命令)时发生的事情:

First. It can give you completely different output.

第一。它可以给你完全不同的输出。

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Second. It can give you no output at all.

第二。它可以给你任何输出。

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found

#23


0  

checkexists() {
    while [ -n "$1" ]; do
        [ -n "$(which "$1")" ] || echo "$1": command not found
        shift
    done
}

#24


0  

I use this because it's very easy:

我用这个是因为它很简单:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

or

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

It uses shell builtin and program echo status to stdout and nothing to stderr by the other hand if a command is not found, it echos status only to stderr.

它使用shell builtin和程序echo status为stdout,而如果没有找到命令,它就不会对stderr产生任何影响,它只对stderr进行回波状态。

#25


0  

In case you want to check if a program exists and is really a program, not a bash built-in command, then command, type and hash are not appropriate for testing as they all return 0 exit status for built-in commands.

如果您想检查一个程序是否存在,是否真的是一个程序,而不是一个bash内置命令,那么命令、类型和散列都不适合测试,因为它们都返回了内置命令的0退出状态。

For example, there is the time program which offers more features than the time built-in command. To check if the program exists, I would suggest using which as in the following example:

例如,有一个时间程序,它提供了比内置命令更多的特性。为了检查程序是否存在,我建议使用以下示例:

# 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

#26


0  

Script

脚本

#!/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"'

Result

结果

✘ [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.

#27


-1  

I had to check if git was installed as part of deploying our CI server. My final bash script was as follows (Ubuntu server):

我必须检查git是否安装作为部署CI服务器的一部分。我的最后一个bash脚本如下(Ubuntu服务器):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

Hope this help someone else!

希望这能帮助别人!

#28


-1  

I couldn't get one of the solutions to work, but after editing it a little I came up with this. Which works for me:

我无法找到一个解决方案,但在编辑后,我想出了这个。这工作对我来说:

dpkg --get-selections | grep -q linux-headers-$(uname -r)

if [ $? -eq 1 ]; then
        apt-get install linux-headers-$(uname -r)
fi

#29


-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