Catch "command not found" from shell script
Solution 1
When a command is not found, the exit status is 127. You could use that to determine that the command was not found:
until
printf "Enter a command: "
read command
"$command"
[ "$?" -ne 127 ]
do
echo Try again
done
While commands generally don't return a 127 exit status (for the very case that it would conflict with that standard special value used by shells), there are some cases where a command may genuinely return a 127 exit status: a script whose last command cannot be found.
bash
and zsh
have a special command_not_found_handler
function (there's a typo in bash
's as it's called command_not_found_handle
there), which when defined is executed when a command is not found. But it is executed in a subshell context, and it may also be executed upon commands not found while executing a function.
You could be tempted to check for the command existence beforehand using type
or command -v
, but beware that:
"$commands"
is parsed as a simple commands and aliases are not expanded, while type
or command
would return true for aliases and shell keywords as well.
For instance, with command=for
, type -- "$command"
would return true, but "$command"
would (most-probably) return a command not found error.
which
may fail for plenty of other reasons.
Ideally, you'd like something that returns true if the command exists as either a function, a shell builtin or an external command.
hash
would meet those criteria at least for ash
and bash
(not yash
nor ksh
nor zsh
). So, this would work in bash
or ash
:
while
printf "Enter a command: "
read command
do
if hash -- "$command" 2> /dev/null; then
"$command"
break
fi
echo Try again
done
One problem with that is that hash
returns true also for a directory (for a path to a directory including a /
). While if you try to execute it, while it won't return a command not found error, it will return a Is a directory
or Permission Denied
error. If you want to cover for it, you could do:
while
printf "Enter a command: "
read command
do
if hash -- "$command" 2> /dev/null &&
{ [ "${command#*/}" != "$command" ] || [ ! -d "$command" ];}
then
"$command"
break
fi
echo Try again
done
Solution 2
-
trap
is only for signals, which are defined insignal(7)
. Not finding a command is simply a failure in theexec
family of functions, which'll return-1
, not send a signal. -
A better way to catch non-existent commands would be to do something like this.
if ! type "$command" >/dev/null 2>&1; then echo "Try again, wrong command" 1>&2 # should output to stderr, not stdout else "$command" fi
Solution 3
Here's another way that uses Stéphane Chazelas's code but with type
, and overcomes type
's limitations...
function isCommand () {
#
local _arg=" $(type -t "$1") "
local _executables=' file alias keyword function builtin '
#
[[ "${_executables#*$_arg}" != "$_executables" ]] && return 0
### if $_arg is NOT in $_executables, the two strings will be identical
#
return 1
}
while
printf "Enter a command: "
read command
do
isCommand "$command" && { "$command"; break; }
#
echo Try again
done
Notes
- In isCommand()...
- ... the variables are padded to avoid partial matches.
- ...
type
returns "file" for any file with the execution bit set. This is the way we detect external commands. - ... this test for inclusion in a string is one of the most non-intuitive I know of. But it's fast and uses no external commands so I use it and wrap a more intuitive function around it. There are a number of other ways to do this as well.
-
isCommand "$command" && { "$command"; break; }
This uses a command list for if-then execution logic. (see bash manpage under Shell Grammer, Lists)-
Advantages
- ... faster execution than the normal
if[[...]]
construct - ... avoids complicated (and error-prone) multi-test logic
- ... similar to OOP
try/catch
exception handling
- ... faster execution than the normal
-
Caveat
- ... In the "then" part following the
&&
or||
, multiple commands must be enclosed in braces and the last command and its arguments must be terminated with a semicolon;
as in{ cmd1 args; cmd2 args; }
.
- ... In the "then" part following the
-
Advantages
Related videos on Youtube
Phil_Charly
Kallithea city rocks. Listening Xatzifrageta now, plz not disturb.
Updated on September 18, 2022Comments
-
Phil_Charly over 1 year
I have a shell script
echo "Type your command" read command echo "You typed $command" $command
so it's simple it runs a command.My question is if the input is wrong suppose
lw
the terminal sayscommand not found
so how can I retrieve this information to my shell script and print to terminalTry again wrong command
. Do I have to redirect the output of the command to a certain file and read or is there any kind of trap signal which is passed to my script.Which is your advice on how to do that it in the most efficient way.-
41754 over 10 yearsDepending on things you may start by something like changing
$command
for$command 2>&1 | grep ": command not found"
-
Olivier Dulac over 10 years@uprego : this will have a side effect of no longer displaying the normal output of 'command', and any error messages as well, as it only keeps lines containing ": commant not found" and no others.
-
41754 over 10 years@OlivierDulac you are incorrectly assuming that the questioner is wanting to run a command that produces standard output or error.
-
Olivier Dulac over 10 years@uprego: ?? I think you are incorrectly assuming he doesn't want to see any output apart from 'command not found' ...
-
41754 over 10 years@OlivierDulac I'm the one no making assumptions. If that solution does not work for him, he is invited to build up a more advanced solution using combinations of the
type
, which is often a shell builtin; andfile
, which is often a /usr/bin/ program. -
Phil_Charly over 10 yearsChill out I didn't state whether I wanted to show the command or not the following answer works for me.@uprego I made a script with your command it prints always
command not found
,I am new to this so please excuse me if I make some obvious mistakes
-
-
slm over 10 yearsI'd also encourage you to use the
type <cmd>
and/orcommand -v <cmd>
commands instead ofwhich
. unix.stackexchange.com/questions/85249/… -
Stéphane Chazelas over 10 yearsSome shells like ksh, zsh and bash can trap on the special ERR non-signal. Not quoting your variables doesn't make sense here. Note all shell commands are executed by exec functions.
-
Stéphane Chazelas over 10 yearsNote that with
bash
, that would not say that-aaaa
for instance is a wrong command.type -- "$command"
in POSIX shells would be the correct syntax, but beware thatash
, even recent versions is not POSIX (well Unix sincetype
is optional in POSIX) in that regard. You may want to usecommand -v instead
(orhash
, see my answer).