How to define environment variable in input command of su - bash

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'

Related

Is it possible using "su -c" with multiple commands but in one session?

I am trying run the following two command in one command.
eval "$(ssh-agent)"
ssh add ~/.ssh/id_rsa
I tried with many possible solutions:
su username -c "{ eval $(ssh-agent -s) }; ssh-add ~/.ssh/id_rsa"
su username -c "eval $(ssh-agent -s)" ; ssh-add ~/.ssh/id_rsa
su username -c "eval $(ssh-agent -s)" && ssh-add ~/.ssh/id_rsa
su username -c "eval $(ssh-agent -s)" && "ssh-add ~/.ssh/id_rsa"
It seems like the first command run successfully but the second either response with Permission denied message (means it run with the current user) or cannot connect to the authentication agent (means it probably created a new session for the second command).
Error messages:
Could not open a connection to your authentication agent.
Error connecting to agent: Permission denied
If I run them separately in order, it works:///
The purpose is to create a bash script with these commands with variables, something like this:
folder=/home/q12
username=q12
su $username -c "{ eval $(ssh-agent -s) }; ssh-add $folder/.ssh/id_rsa"
Because of the variables I can not quote the whole command because it will be sensitive to ";" and "&&".
Can anyone help me with this?
Thank you!
You need single quotes. Otherwise the command substitution is evaluated in the current context.
su username -c 'eval "$(ssh-agent -s)"; ssh-add ~/.ssh/id_rsa'
Edit:
To get conditional execution of ssh-add, you can do:
su username -c 'script=$(ssh-agent -s) || exit 1; eval "$script"; ssh-add ~/.ssh/id_rsa'
# or
su username -c 'set -e; script=$(ssh-agent -s); eval "$script"; ssh-add ~/.ssh/id_rsa'
The argument in su -c STRING is a command string, similar to bash -c STRING. We can also nest double quotes inside single quotes.
The first of your tries is closest, but inside double-quotes things like $(ssh-agent -s) get evaluated by your shell before they're passed to su as part of an argument. Net result: the ssh agent is running under the current user, so the other user can't use it.
You need to delay evaluation until the other-user shell. Using single-quotes instead of double would do this, but in the actual command you have $folder, which you clearly want evaluated by your shell (it won't be defined in the other-user shell), so you don't want to delay evaluation of that. The simplest way to do this is to escape the $ that you want to delay evaluation of (your shell will remove the escape, so the other-user shell will see & evaluate it):
su "$username" -c "eval \$(ssh-agent -s); ssh-add $folder/.ssh/id_rsa"
# ^ Note the escape
(BTW, I also added double-quotes around the username, as that's generally-good scripting hygiene. Quoting $folder is more complicated, and shouldn't be necessary as long as it doesn't contain any weird characters, so I skipped it. Also, the { } weren't necessary, and if they were used there needed to be a ; before the }... so I just removed them.)
Another option is to single-quote part of the command and double-quote another part (mixed quoting looks weird, but is perfectly legal in shell syntax):
su "$username" -c 'eval $(ssh-agent -s);'" ssh-add $folder/.ssh/id_rsa"
# ^ single-quoted part ^^ double-quoted part ^
The reason the rest of your attempts didn't work is that the delimiter between the commands (; or &&) wasn't in the quoted string, and hence was treated as a delimiter by your shell, so the second command was run under your user ID rather than as part of the su command.

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"'

Why sudo -H needs an extra command?

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.

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"

How to set $TERM to a value when running /bin/bash via command line?

When I run the /bin/bash process with 2 parameters -c and SomeUserInput,
where SomeUserInput is echo $TERM
The output is
xterm-256color
Is there a way I can set the value of $TERM via a command line parameter to /bin/bash so the above invokation of echo $TERM would print something else that I specify?
(Yes, I've done a lot of digging in man bash and searching elsewhere, but couldn't find the answer; although I think it's likely there.)
First of all, since you used double quotes, that prints the value of TERM in your current shell, not the bash you invoke. To do that, use /bin/bash -c 'echo $TERM'.
To set the value of TERM, you can export TERM=linux before running that command, set it only for that shell with either TERM=linux /bin/bash -c 'echo $TERM' (shell expression), or /usr/bin/env TERM=linux /bin/bash -c 'echo $TERM' (execve compatible (as for find -exec)).
Update:
As for your edit of only using command line parameters to /bin/bash, you can do that without modifying your input like this:
/bin/bash -c 'TERM=something; eval "$1"' -- 'SomeUserInput'
Well, you can either set the variable on your .bashrc file, or simply set with the bash invocation:
/bin/bash -c "TERM=something-else; echo $TERM"

Resources