Linux shell script: Run a program only if it exists, ignore it if it does not exist
Solution 1
My interpretation would use a wrapper function named the same as the tool; in that function, execute the real tool if it exists:
figlet() {
command -v figlet >/dev/null && command figlet "$@"
}
Then you can have figlet arg1 arg2...
unchanged in your script.
@Olorin came up with a simpler method: define a wrapper function only if we need to (if the tool doesn't exist):
if ! command -v figlet > /dev/null; then figlet() { :; }; fi
If you'd like the arguments to figlet
to be printed even if figlet isn't installed, adjust Olorin's suggestion as follows:
if ! command -v figlet > /dev/null; then figlet() { printf '%s\n' "$*"; }; fi
Solution 2
You can test to see if figlet
exists
if type figlet >/dev/null 2>&1
then
echo Figlet is installed
fi
Solution 3
A common way to do this is with test -x
aka [ -x
. Here is an example taken from /etc/init.d/ntp
on a Linux system:
if [ -x /usr/bin/lockfile-create ]; then
lockfile-create $LOCKFILE
lockfile-touch $LOCKFILE &
LOCKTOUCHPID="$!"
fi
This variant relies on knowing the full path of the executable. In /bin/lesspipe
I found an example which works around that by combining -x
and the which
command:
if [ -x "`which bunzip`" ]; then bunzip -c "$1"
else echo "No bunzip available"; fi ;;
That way this will work without knowing in advance where in the PATH
the bunzip
executable is.
Solution 4
At the start of your script, check if figlet
exists, and if it does not, define a shell function that does nothing:
type figlet >/dev/null 2>&1 || figlet() { :; }
type
checks if figlet
exists as a shell built-in, function, alias, or keyword, >/dev/null 2>&1
discards stdin and stdout so you don't get any output, and if it does not exist, figlet() { :; }
defines figlet
as a function that does nothing.
This way you don't have to edit every line of your script that uses figlet
, or check if it exists every time figlet
is called.
You can add a diagnostic message, if you like:
type figlet >/dev/null 2>&1 || { echo 'figlet not installed.' ; figlet() { :; } ; }
As a bonus, since you didn't mention which shell you are using, I believe this is POSIX compliant, so it should work on most any shell.
Solution 5
Another alternative -- a pattern I've seen in project auto configure scripts:
if [ -x /usr/bin/figlet ]
then
FIGLET=/usr/bin/figlet
else
FIGLET=:
fi
$FIGLET "Hello, world!"
In your specific case you could even do,
if [ -x /usr/bin/figlet ]
then
SAY=/usr/bin/figlet
elif [ -x /usr/local/bin/figlet ]
then
SAY=/usr/local/bin/figlet
elif [ -x /usr/bin/banner ]
then
SAY=/usr/bin/banner
else
SAY=/usr/bin/echo
fi
$SAY "Hello, world!"
If you don't know the specific path, you can try multiple elif
(see above) to try known locations, or just use the PATH
to always resolve the command:
if command -v figlet >/dev/null
then
SAY=figlet
elif command -v banner >/dev/null
then
SAY=banner
else
SAY=echo
fi
In general, when writing scripts, I prefer to only call commands in specific locations specified by me. I don't like the uncertainty/risk of what the end user might have put into their PATH
, perhaps in their own ~/bin
.
If, for example, I was writing a complicated script for others that might remove files based on the output of a particular command I'm calling, I wouldn't want to accidentally pick up something in their ~/bin
that might or might not be the command I expected.
Related videos on Youtube
Arlene Mariano
Updated on September 18, 2022Comments
-
Arlene Mariano over 1 year
I am programming a Linux shell script that will print status banners during its execution only if the proper tool, say
figlet
, is installed (this is: reachable on system path).Example:
#!/usr/bin/env bash echo "foo" figlet "Starting" echo "moo" figlet "Working" echo "foo moo" figlet "Finished"
I would like for my script to work without errors even when
figlet
is not installed.What could be a practical method?
-
gip about 5 years
-
Arlene Mariano about 5 years@sudodus : just ignoring the 'figlet' (and its parameters) command would be OK. Continuing execution, of course.
-
g_uint about 5 yearsThe title to this question got me in all sorts of metaphysical problems
-
Giacomo Alzetta about 5 yearsDo you want to ignore all errors? Just use
figlet ... || true
. -
eckes about 5 yearsIf you don’t care about exit codes a shortcut is to use
figlet || true
, but in your case probably a shell function which Echos plaintext If no Banner can be printed is more likely what you want.
-
-
Vercingatorix about 5 yearsWhere does
command
come from? I don't have it in my installations (Red Hat 6.8 Enterprise and Cygwin64). -
wyrm about 5 years
command
is a POSIX Bourne shell builtin. It normally executes the command provided, but the-v
flag makes it behave more liketype
, another shell builtin. -
l0b0 about 5 years@eewanco
type -a command
will show you.which
will only show executables on your$PATH
, not built-ins, keywords, functions or aliases. -
Gilles 'SO- stop being evil' about 5 yearsDon't use
which
. And even ifwhich
worked, usingtest -x
on its output is silly: if you get a path fromwhich
, it exists. -
Gilles 'SO- stop being evil' about 5 yearsWhat if
figlet
was in/usr/local/bin
or/home/bob/stuff/programs/executable/figlet
? -
kgendron about 5 yearsThe problem here is that figlet can fail for reasons besides not being installed, so just testing the exit code doesn't suffice.
-
nigel222 about 5 yearsIf you really want to test only for its installation, then you want to test if
$?
equals 127 (on my linux system). 127 is "command not found". But my thinking is that for rational commands, ifcommand --help
fails, then the installation is borked sufficiently that it might as well not be there!. -
kgendron about 5 years126 is "command found but not executable," so you would want to test against that as well. Unfortunately, you can't depend on
--help
being available. Posix utilities don't have it, for one, and posix guidelines actually recommend against it.at --help
fails withat: invalid option -- '-'
, for instance, and an exit status of130
on my system. -
dave_thompson_085 about 5 years@l0b0: on RedHat-family with bash and the default profile(s)
which
does find an applicable alias, because it aliaseswhich
itself to run/usr/bin/which
and pipe it a list of the shell's aliases to look in (!) (Of course\which
suppresses the alias and uses only the program, which doesn't show aliases.) -
kgendron about 5 yearsAlso, of course, if figlet can exit with status 127 that could be a problem too.
-
comfreak about 5 years@Gilles: Technically speaking,
test -x
does more than just check if a file exists (that's whattest -e
is for). It also checks if the file has the execute permissions set. -
Gilles 'SO- stop being evil' about 5 years@comfreak So does
which
. -
gokhan acar about 5 yearsI like the simplicity of this answer. You could also replace
type figlet >/dev/null 2>&1
withhash figlet 2>/dev/null
if you're using bash. (The OP said "if it's in my PATH".)