
You'll find quite a few different methods suggested on the internet, and they all have their own problems.
Before I go into the details, this is the syntax you want:
if command -v <command_name> &> /dev/null; then
echo "command exists"
fior alternatively:
command -v <command_name> &> /dev/null && echo "command exists"Explanation
Here's the man page for command -v command_name:
-v Write a string to standard output that indicates the
pathname or command that will be used by the shell, in
the current shell execution environment (see Section
2.12, Shell Execution Environment), to invoke
command_name, but do not invoke command_name.
* Utilities, regular built-in utilities,
command_names including a <slash> character, and
any implementation-defined functions that are found
using the PATH variable (as described in Section
2.9.1.1, Command Search and Execution), shall be
written as absolute pathnames.
* Shell functions, special built-in utilities,
regular built-in utilities not associated with a
PATH search, and shell reserved words shall be
written as just their names.
* An alias shall be written as a command line that
represents its alias definition.
* Otherwise, no output shall be written and the exit
status shall reflect that the name was not found.
The main takeaway here is this method will work for executables in $PATH, functions, and aliases. Other methodologies only work for a subset of those commands.
The command command is also POSIX-compliant, meaning it should work consistently across different UNIX variants.
The problem with which
One method you'll see suggested in help forums is which <program>. This method works for both functions and aliases, but it doesn't define a consistent exit code behavior. which will exit with a non-zero status code on most distros, but this isn't a guarantee.
which foo &> /dev/null && echo "'foo' maybe exists?"The problem with if [[ -x file ]]
One of the more common methods I've seen suggested is:
if [[ -x "$(command -v <command_name>)" ]]; then
echo "executable exists"
fiThis will test if the output of the command -v <command_name> is an executable file or not, which won't work for aliases. If you want to check for the existence of executables, there are better options such as find <dir> -type f -executable or the pinpoint function from "Reliably Finding Executables in $PATH".

Reliably Finding Executables in $PATH
Aug 27, 2021 · 4 min read
Most built-in commands commonly used to find executables in $PATH don't always work quite as expected, or are shell-specific.
if [[ -x file ]] doesn't work for aliases, which may be used to intentionally shadow executables:
$ if [[ -x "$(command -v grep)" ]]; then echo "grep exists"; fi
grep exists
$ alias grep='grep --color=auto'
$ if [[ -x "$(command -v grep)" ]]; then echo "grep exists"; fibut it does work for functions, which may cause confusion:
$ if [[ -x "$(command -v docker)" ]]; then echo "docker exists"; fi
docker exists
$ docker() { echo "do some prework"; command docker "$@" }
$ if [[ -x "$(command -v docker)" ]]; then echo "docker exists"; fi
docker existsSee "Automatically Execute Code Before & After Unix Commands" for more tricks on using functions to shadow executables.

Automatically Execute Code Before & After Unix Commands
Jan 19, 2023 · 4 min read
It can be helpful to run some code automatically before or after calling a command, and it is easy to accomplish with shadowing functions.
Example usages
To check for the nonexistence of a command you can use ! command -v <command_name> syntax:
if ! command -v beep &> /dev/null; then
alias beep="echo -ne '\007'"
fiFor a real world example, I have my macOS dotfiles install any missing tools I use frequently using Homebrew :
if command -v brew &> /dev/null; then
command -v gawk > /dev/null || brew install gawk
command -v gsed > /dev/null || brew install gnu-sed
command -v jq > /dev/null || brew install jq
command -v tree > /dev/null || brew install tree
command -v wget > /dev/null || brew install wget
fiI also alias pip3 to pip when pip doesn't exist because the version suffix on Python commands is wildly inconsistent across distros and package managers:
if ! command -v pip &> /dev/null && command -v pip3 &> /dev/null; then
alias pip=pip3
fiYou can also mix command -v <command_name> conditionals with other Bash conditionals like this :
if ! command -v brew &> /dev/null && [[ -f /opt/homebrew/bin/brew ]]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
fi


