I'm assuming your cron email settings are all correct, and you otherwise get emails.
Your sending all stdout to /dev/null
, so anything that prints error messages must output them to stderr. You might want to make sure everything in the script is outputting correctly.
At times I have had to work with third party code, which was sending everything to stdout. In those cases, I usually hack up an error check. It's not pretty, but neither are most shell scripts.
Then answer is that sudo
has a bug. First, the workaround: I put this in my /etc/sudoers.d/zabbix file
:
zabbix ALL=(root) NOPASSWD: /bin/env SHELL=/bin/sh /usr/local/bin/zabbix_raid_discovery
and now subcommands called from zabbix_raid_discovery
work.
A patch to fix this will be in sudo 1.8.15. From the maintainer, Todd Miller:
This is just a case of "it's always been like that". There's not
really a good reason for it. The diff below should make the behavior
match the documentation.
- todd
diff -r adb927ad5e86 plugins/sudoers/env.c
--- a/plugins/sudoers/env.c Tue Oct 06 09:33:27 2015 -0600
+++ b/plugins/sudoers/env.c Tue Oct 06 10:04:03 2015 -0600
@@ -939,8 +939,6 @@
CHECK_SETENV2("USERNAME", runas_pw->pw_name,
ISSET(didvar, DID_USERNAME), true);
} else {
- if (!ISSET(didvar, DID_SHELL))
- CHECK_SETENV2("SHELL", sudo_user.pw->pw_shell, false, true);
/* We will set LOGNAME later in the def_set_logname case. */
if (!def_set_logname) {
if (!ISSET(didvar, DID_LOGNAME))
@@ -984,6 +982,8 @@
if (!env_should_delete(*ep)) {
if (strncmp(*ep, "SUDO_PS1=", 9) == 0)
ps1 = *ep + 5;
+ else if (strncmp(*ep, "SHELL=", 6) == 0)
+ SET(didvar, DID_SHELL);
else if (strncmp(*ep, "PATH=", 5) == 0)
SET(didvar, DID_PATH);
else if (strncmp(*ep, "TERM=", 5) == 0)
@@ -1039,7 +1039,9 @@
if (reset_home)
CHECK_SETENV2("HOME", runas_pw->pw_dir, true, true);
- /* Provide default values for $TERM and $PATH if they are not set. */
+ /* Provide default values for $SHELL, $TERM and $PATH if not set. */
+ if (!ISSET(didvar, DID_SHELL))
+ CHECK_SETENV2("SHELL", runas_pw->pw_shell, false, false);
if (!ISSET(didvar, DID_TERM))
CHECK_PUTENV("TERM=unknown", false, false);
if (!ISSET(didvar, DID_PATH))
Best Answer
set -e
doesn't trigger on failing commands that are used as conditions like in the condition section ofif
/while
/until
constructs or on the left of a||
,&&
or in functions, subshells, sourced files,eval
ed code that are invoked under those conditions.If it did, then:
Would exit the script if
/custom.log
was a regular file as[
would then also exit with a non-zero exit status.The
[
builtin command of thebash
shell (and most other implementations) exits with a1
status if the tested condition is not met, and2
if there's a syntax error (not all syntax errors though, for instance, not in[ -v 'a[+]' ]
). POSIX requires the exit status to be greater than 1 in case of error.So you could choose to exit the script if a command exits with a code greater than 1 regardless of whether it's used in a condition or not with something like:
Note that you can't use the
ERR
trap for that as theERR
trap is only run in the same condition as those that trigger the exit byset -e
.Now, beware of the implications. For instance, that would cause a:
to exit if
/file
didn't exist or wasn't readable, asgrep
returns with a 2 status in that case, even though with-s
, the intention was clearly to ignore those cases.So you'll need to beware of upon which conditions the commands you use in your conditions may exit with a status greater than 1. To work around those, you'd need something like:
You could restrict the exit upon exit status greater than 1 to the
[
ortest
command with something like:That has a few limitations. In
That would cause the subshell to exit, but not the parent as the
$previous_BASH_COMMAND
has not been set there. And in:The shell would be exited upon running
echo here
, because$?
would be 2 and$previous_BASH_COMMAND
was[ -f / ]
.In any case, things like
could not be detected as the exit status is not propagated to the parent shell process (except with the
pipefail
option in the first case).Now, I'm not sure it's worth the trouble adding this kind of (brittle) detection at run time, when the error is easily detectable at development time (when you write and test your script).