I have a little maintenance thing to do as super user, from time to time. I would like that to be a one-liner, for easy copying from my self-documentation to a terminal. I reduced it to the bare essentials of where it goes wrong; it involves setting and using a shell variable. See for yourselves:
$ sudo bash -c 'var=VAL; echo "$USER $UID $HOME ** $var **"; declare -p var;' root 0 /home/myuser ** VAL ** declare -- var="VAL" You'll of course notice that I didn't give sudo it's -i option, and therefor the HOME variable was inapropriate. So I repeated the command with sudo -i:
$ sudo -i bash -c 'var=VAL; echo "$USER $UID $HOME ** $var **"; declare -p var;' root 0 /root ** ** declare -- var="VAL" And there it is: although declare declares that var does have the value VAL assigned to it, echo does not echo it. How can that be?
And by the way, I tried the same command, but starting with declare -p var; ..., to make sure var did not double with some read-only environment or shell special variable; it gives bash: line 0: declare: var: not found, as expected.
For completeness I did the same in a multiliner: opening a proper shell prompt and typing several command lines:
$ sudo -i bash [sudo] password for myuser: # var=VAL # echo "$USER $UID $HOME ** $var **" root 0 /root ** VAL ** This works as it should be, except that I have to copy and paste line by line.
Can anyone explane why, in the one-liner with sudo -i bash -c, a variable can be set, and is declared to be set, but expands to nothing?
$ sudo --version Sudo version 1.8.21p2 Sudoers policy plugin version 1.8.21p2 Sudoers file grammar version 46 Sudoers I/O plugin version 1.8.21p2 $ bash --version GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu) But here is what @terdon asked, and I never tinkered with that:
$ sudo grep '^[^#].' /etc/sudoers [sudo] password for myuser: Defaults env_reset Defaults mail_badpass Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" root ALL=(ALL:ALL) ALL %admin ALL=(ALL) ALL A similar question here on StackExchange explains the unexpected behaviour, as was the question: sudo -i starts an extra shell first, in comparison with plain sudo, and it is that extra shell that first interprets the bash -c 'command ...' as a string, encountering the same problems you will have when you try to insert a plain string containing $'s and`'s into a command.
So I took the super user thing a step further, and added su to the command to make it do what I want it to do; mostly you will want to use su --version, but the su-version in this case works fine too:
$ sudo su - root -c 'var=VAL; echo "$USER $UID $HOME ** $var **"; declare -p var;' root 0 /root ** VAL ** declare -- var="VAL" $ sudo su root -c 'var=VAL; echo "$USER $UID $HOME ** $var **"; declare -p var;' root 0 /root ** VAL ** declare -- var="VAL"
sudo bash -c 'var=VAL; echo "$USER $UID $HOME ** $var **";'printsroot 0 /root ** VAL **as expected. Have you perhaps aliasedsudotosudo -E? What's the output oftype sudo? If not, please share the output ofsudo grep '^[^#].' /etc/sudoers. You must be telling it to keep the$HOMEvariable.$ type sudogivessudo is /usr/bin/sudo. And about the HOME variable, in the 10 years that I do linux, I have never known other that to needsudo -ito have it set to/root. I'll add the sudo grep thing to the question, I can't format multiline here in the comments.HOME, it should always be reset to the home dir of the user you switched to. Are you on Ubuntu, perhaps? I think they do some weird things.