Why sudo -H needs an extra command? - bash

I found this related question What does sudo -H do?
that demonstrates the usage of -H with
sudo -H bash -c 'echo $HOME $USER'
But I don't understand is why is bash necessary, because the echo command can stand on its own, but running
sudo -H echo $HOME $USER
will display instead current user variables (and not root's).
So why is (in this case) bash needed for the commands ran as sudo arguments to take in the -H flag?
This is also true if one specifies another user, like sudo -H -u anotheruser echo $HOME $USER.

When you run the command:
sudo -H echo $HOME $USER
the variables $HOME and $USER are evaluated by the current shell. They are replaced with their values and the command line adjusted this way is passed to sudo.
F.e. if your username is foo and your home is /home/foo, the command above is the same thing as:
sudo -H echo /home/foo foo
It's not necessarily bash that is required to get what you want. It's required a way to evaluate the environment variables by the command that is executed by sudo, not by the current shell.
When you run:
sudo -H bash -c 'echo $HOME $USER'
the $HOME and $USER environment variables are not evaluated by the current shell, because they are enclosed in single quotes.
sudo receives the command line as it is written here (it's fourth argument is the string echo $HOME $USER as a single word). It then launches bash with two arguments: -c and the string mentioned above.
bash interprets -c as "execute a command" that is provided as the next argument. It then tries to run echo $HOME $USER (notice that the variables are not enclosed in quotes here) and, before running echo it replaces $HOME and $USER with their values (that belong to the new user now).
Running bash (or any other shell) is needed to do the variables expansion in the execution environment of the new user.

$HOME and $USER are expanded by the shell before sudo even runs. You need bash -c to add a layer of indirection, so that the shell run by sudo does the expansion.

Related

echo "root" when using $USER

I'd like to understand bash better and when variables are evaluated.
When using sudo the user environment that can be checked out using env is exchanged with the root environment. Hence env and sudo env yield different results.
Now if I am doing sudo echo "I am $USER"! the result is I am my-username!
instead of I am root!, presumably because the $USER variable is looked up before the sudo command is excuted.
How can I use that same command so that I am root! is printed? Do I need some switches or change the string somehow?
In the command sudo echo "$USER", bash evaluates $USER first and then executes the command. In that case your username is printed.
If you want to print the root username, the parameter expansion must be done as the root user. It can be done using bash -c command:
sudo bash -c 'echo "$USER"'

Run a subshell as root

Consider you have a Linux/UNIX machine with Bash. You have a file secret.txt that only root can read. You want to use a command that takes a string as an argument, say,
sample-command <string>
Log in as a root user and run the command using the first line of the text file:
root ~ $ sample-command $(sed '1!d' secret.txt)
Can this be done by non-root, sudoer users?
Note. sudo sh -c "<command>" doesn't help since subshells don't carry over the root/sudo privilege. For example,
sarah ~ $ sudo sh -c "echo $(whoami)"
gives you sarah, not root.
Expansions like command substitution will be processed by the shell before executing the actual command line:
sudo sh -c "echo $(whoami)"
foouser
Here the shell will first run whoami, as the current user, replace the expansion by it's result and then execute
sudo sh -c "echo foouser"
Expansions doesn't happen within single quotes:
sudo sh -c 'echo "$(whoami)"'
root
In this example $(whoami) won't get processed by calling shell because it appears within single quotes. $(whoami) will therefore get expanded by subshell before calling echo.

Echo variable using sudo bash -c 'echo $myVariable' - bash script

I want to echo a string into the /etc/hosts file. The string is stored in a variable called $myString.
When I run the following code the echo is empty:
finalString="Hello\nWorld"
sudo bash -c 'echo -e "$finalString"'
What am I doing wrong?
You're not exporting the variable into the environment so that it can be picked up by subprocesses.
You haven't told sudo to preserve the environment.
\
finalString="Hello\nWorld"
export finalString
sudo -E bash -c 'echo -e "$finalString"'
Alternatively, you can have the current shell substitute instead:
finalString="Hello\nWorld"
sudo bash -c 'echo -e "'"$finalString"'"'
You can do this:
bash -c "echo -e '$finalString'"
i.e using double quote to pass argument to the subshell, thus the variable ($finalString) is expanded (by the current shell) as expected.
Though I would recommend not using the -e flag with echo. Instead you can just do:
finalString="Hello
World"

where did my environment variable go?

I am trying to use an environment variable in a bash script that needs to run as sudo with source.
I have the following file (my_file.sh)
echo "this is DOMAIN = $DOMAIN"
I have the DOMAIN environment variable in my session..
and now I need to run
sudo -E bash -c "source ./my_file.sh"
but the output does not display the value for $DOMAIN. instead it is empty.
if I change the command to be
sudo -E bash -c "echo $DOMAIN"
I see the correct value..
what am I doing wrong?
With the command line:
sudo -E bash -c "source ./my_file.sh"
you are running a script that may refer to environment variables that would need to be exported from a parent shell to be visible.
On the other hand:
sudo -E bash -c "echo $DOMAIN"
expands the value of $DOMAIN in the parent shell, not inside your sudo line.
To demonstrate this, try your "working" solution with single quotes:
sudo -E bash -c 'echo $DOMAIN'
And to make things go, try exporting the variable:
export DOMAIN
sudo -E bash -c "source ./my_file.sh"
Or alternately, pass $DOMAIN on the command line:
sudo -E bash -c "source ./my_file.sh $DOMAIN"
And have your script refer to $1.

How to define environment variable in input command of su

This command has an empty output.
su user -c "ABC=abc;echo $ABC"
Any idea, how can I define a variable in the input command?
Change your quotes to single quotes. The double quotes allow the variable to be substituted in the current environment where it's not set yet. To see the difference, try your version with $USER and compare it to this one:
su user -c 'ABC=abc; echo $ABC; echo $USER'
If using a bourne shell variant:
ABC=abc su user -c 'echo $ABC'
If not, use env.
env ABC=abc su user -c 'echo $ABC'

Resources