I'd like to make a particular bash script failfast when it cannot find a command, while retaining globally the usual friendly command_not_found behavior. E.g., if I save the following to /tmp/foo.sh
, …
# ----------------------------start foo.sh----------------------------
THIS_FP="$0"
THIS_FN="$(basename ${THIS_FP})"
THIS_DIR="$(dirname ${THIS_FP})"
function setup {
for CMD in \
'foo' \
; do
echo -e "\n$ ${THIS_FN}::${FUNCNAME[0]}::${CMD}"
eval "${CMD}"
done
} # end function setup
function teardown {
for CMD in \
"ls -alt ${THIS_DIR} | head" \
; do
echo -e "\n$ ${THIS_FN}::${FUNCNAME[0]}::${CMD}"
eval "${CMD}"
done
} # end function teardown
for CMD in \
'setup' \
'teardown' \
; do
echo -e "\n$ ${THIS_FN}::main loop::${CMD}"
eval "${CMD}"
done
# ------------------------------end foo.sh----------------------------
… and make it executable, and run it, I get
# ----------------------------not what I want-------------------------
me@it:~$ /tmp/foo.sh
$ foo.sh::main loop::setup
$ foo.sh::setup::foo
/tmp/foo.sh: line 10: foo: command not found
$ foo.sh::main loop::teardown
$ foo.sh::teardown::ls -alt /tmp | head
total 68840
drwxrwxrwt 22 root root 1600 May 6 18:49 .
-rwxr-xr-x 1 me me 527 May 6 18:49 foo.sh
drwx------ 2 me me 40 May 6 17:54 plugtmp-1
srw------- 1 me me 0 May 6 17:28 tramp.13004aZf
drwx------ 2 me me 4200 May 6 17:22 matecorba-me
drwx------ 2 me me 40 May 6 02:20 plugtmp
-rw------- 1 me me 2034335 May 4 14:21 s_TTRuhW.mp3.part
-rw------- 1 me me 1658381 May 2 12:21 +rM4IttD.mp3.part
-rw-r--r-- 1 me me 86420 May 1 19:22 duplicity_20130501_1917.txt
# ----------------------------not what I want-------------------------
Instead, I want to make only this script foo.sh
fail, and to fail as soon as it encounters the 'command not found': e.g.,
# ------------------------------what I want---------------------------
me@it:~$ /tmp/foo.sh
$ foo.sh::main loop::setup
$ foo.sh::setup::foo
/tmp/foo.sh: ERROR: foo: command not found
# Throwing me back to the shell from which I invoked foo.sh,
# *without* changing the usual friendly behavior outside it, e.g.:
me@it:~$ foo
No command 'foo' found, did you mean:
Command 'fio' from package 'fio' (main)
Command 'xoo' from package 'xoo' (main)
Command 'fop' from package 'fop' (main)
Command 'fox' from package 'objcryst-fox' (main)
Command 'zoo' from package 'zoo' (main)
Command 'goo' from package 'goo' (main)
foo: command not found
# ------------------------------what I want---------------------------
Can I provide this behavior by editing only foo.sh
, without touching .bashrc
or any other currently-installed file ? If so, how? If not, why not?
Best Answer
To have your script exit immediately after error run your script with
bash -e /tmp/foo.sh
or put this line at the beginning of the script:set -e
.-e
orerrexit
tells bash to exit the script immediately if an "error" occurs. Note thaterrexit
is a bit more complicated than that and it does not always do what you might expect. Read this for more information: http://mywiki.wooledge.org/BashFAQ/105 ("Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?")The command not found handling feature is (normally) not enabled inside a bash script. That is because a bash script does not read bashrc and the
command_not_found_handle
is (usually) defined in some bashrc (in debian it is/etc/bash.bashrc
).If you really want to be really sure that the command not found handling feature is disabled in your script you can undefine the function like this:
If you do this inside a bash script and you execute the script, instead of sourcing, then
command_not_found_handle
will only be undefined inside the bash script. The state ofcommand_not_found_handle
in your interactive shell will stay in whatever state it was before executing the script.Read this for more information: https://superuser.com/questions/176783/what-is-the-difference-between-executing-a-bash-script-vs-sourcing-it
And since you are using
eval
please also read: http://mywiki.wooledge.org/BashFAQ/048 ("Eval command and security issues")