Shell variables are initialised from environment variables in every shell, you can't get around that.
When the shell starts, for every environment variable it receives that has a valid name as a shell variable, the shell assigns the corresponding shell variable the corresponding value. For instance, if your script is started as:
env VAR_A=xxx your-script
(and has a #!/bin/bash -
she-bang), env
will execute /bin/bash
and pass VAR_A=xxx
to that bash command, and bash
will assign its $VAR_A
variable the value xxx
.
In the Bourne shell and in the C-shell, if you assign a new value to that shell variable, it doesn't affect the corresponding env variable passed to later commands executed by that shell, you have to use export
or setenv
for that (note however that in the Bourne shell if you unset
a variable, it removes both the shell variable and environment variable).
In:
env VAR=xxx sh -c 'VAR=yyy; other-command'
(with sh
being the Bourne shell, not modern POSIX shells) Or:
env VAR=xxx csh -c 'set VAR = yyy; other-command'
other-command
receives VAR=xxx
in its environment, not VAR=yyy
, you'd need to write it:
env VAR=xxx sh -c 'VAR=yyy; export VAR; other-command'
or
env VAR=yyy csh -c 'setenv VAR yyy; other-command'
For other-command
to receive VAR=yyy
in its environment.
However, ksh
(and POSIX as a result, and then bash
and all other modern Bourne-like shells as a result) broke that.
Upon start-up those modern shells bind their shell variable to the corresponding environment variable.
What that means is that a script may clobber the environment just by setting one of its variables even if it doesn't export it. Some shells are even known to remove the environment variables it cannot map to shell variables (which is why it's recommended to only use shell-mappable variable names for environment variable names).
That's a main reason why by convention, all uppercase variables should be reserved for environment variables.
To work around that, if you want the commands executed by your script to receive the same environment as the shell interpreting your script received, you'd need to store that environment somehow. You can do it by adding:
my_saved_env=$(export -p)
at the start of your script, and then run your commands with:
(eval "$my_saved_env"; exec my-other-command and its args)
In the bosh
and mksh
shells, local variables in functions don't clobber environment variables (as long as you don't export
them), though note that shell builtins which use some special variables (like HOME
for cd
, PATH
for executable lookup...) would use the value of the shell (local) variable, not the environment one.
$ env VAR=env mksh -c 'f() { local VAR=local; echo "$VAR"; printenv VAR; }; f'
local
env
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
No Sudo Option
You can use the source command to run another bash script in the same environment you came from. (Without launching a subprocess)
Script1.sh
Script2.sh
--
With Sudo
The reason your example doesn't work is because of the sudo. You can use Sudo -E option to keep the enviroment variables with the super user environment.
Script1.sh
Script2.sh