Reputation: 667
I have 2 variables PASSWORD="123" and FILEPATH="/dir-1/dir2", both are exported as env vars that visible to all users.
I am trying to echo to a file /dir-1/dir2/file that belongs to root as user1.
I knew the issue that because shell redirect won't work with sudo, i.e. sudo echo <TEXT> >> <FILE>
, so what I did are:
sudo bash -c 'echo "MY.Password $PASSWORD" >> ${FILEPATH}/file'
sudo bash -c 'echo MY.Password $PASSWORD >> ${FILEPATH}/file'
I was expecting to see the last line in the file to be:
MY.Password 123
But although the variable got substituted correctly, the expected content of MY.Password 123
does not appear as the last line in the /dir-1/dir2/file
file.
I then tried
sudo bash -c "echo My.Password '$PASSWORD' >> ${FILEPATH}/file"
sudo bash -c "echo My.Password $PASSWORD >> ${FILEPATH}/file"
Both worked.
Then I start to think if it's because of the single quote, I tried to escape the single quote with double quotes:
sudo bash -c 'echo "'"MY.Password $PASSWORD"'" >> "'"${FILEPATH}"'"/file'
This weird looking command also worked...
Can anyone explain me why my commands don't work, and why the last, weird looking, command worked?
Upvotes: 0
Views: 1987
Reputation: 140900
sudo
doesn't pass your environment by default. sudo
has an option -E
or --preserve-env
that preserves existing environment inside the command run under sudo
. Examples:
# let's set some variable `a`
a=Hello
# will print an empty line
sudo sh -c 'echo $a'
# will print Hello
sudo sh -c "echo $a"
# will print an empty line
sudo -E sh -c 'echo $a'
# because first we need to make `a` exported
export a
# this will print Hello
sudo -E sh -c 'echo $a'
When you use double quotes, the expansion happens before calling sudo
. So expansion/execution goes like this:
$ sudo sh -c "echo $a"
+ sudo sh -c 'echo Hello'
+ sh -c 'echo Hello'
+ echo Hello
Hello
When you use single quotes the expansion/execution goes like this:
$ sudo sh -c 'echo $a'
+ sh -c 'echo $a'
+ echo $a
# now it depends if `$a` exists here, after `sudo` inside `sh`
+ echo
If the variable is not in environment, it will expand to nothing.
sudo bash -c 'echo "MY.Password $PASSWORD" >> ${FILEPATH}/file'
The PASSWORD
and FILEPATH
variables are not set inside sudo
, so they expand to empty strings.
sudo bash -c "echo My.Password '$PASSWORD' >> ${FILEPATH}/file"
First in your shell are the PASSWORD
and FILEPATH
variables expanded. Then sudo
is executed, then bash
is executed. Inside bash
the echo My.Password 'PASSWORD' >> FILEPATH/file
is executed so it works.
sudo bash -c 'echo "'"MY.Password $PASSWORD"'" >> "'"${FILEPATH}"'"/file'
Let's split it with newlines:
sudo bash -c 'echo "'\
"MY.Password $PASSWORD"\
'" >> "'\
"${FILEPATH}"\
'"/file'
Because your variables are inside "
they are expanded before calling sudo. Then inside sudo
they are already expanded and the values are properly quoted, the values are inside "
in the child shell. This is the "most" correct form, because expanded values should be properly quoted, inside "
, so that nothing strange happens when ex. FILEPATH
has a space in it.
I usually do this:
sudo bash -c 'echo MY.Password "$1" >> "$2"/file' -- "$PASSWORD" "$FILEPATH"
It makes me not worry about using "
quotes inside the shell and nonetheless pass properly quoted variables to the shell without changing the environment.
Alternatively, you could do this:
export PASSWORD FILEPATH
sudo -E bash -c 'echo MY.Password "$PASSWORD" >> "$FILEPATH"/file'
Or go for a tea:
echo MY.Password "$PASSWORD" | sudo tee -a "$FILEPATH"/file
Upvotes: 3