Why is this bash variable empty when I just set it? - bash

$ sudo sh -c "FOO=bar echo Result:${FOO}"
Result:
Why is the value stored in FOO not displayed?

Because bash replaces ${FOO} before calling sudo. So what sh -c actually sees is:
FOO=bar echo Result:
Besides, even if you tried
FOO=bar echo Result:${FOO}
It still won't work1. To get that right, you can do:
FOO=bar; echo Result:${FOO}
Now that that is fixed, let's get back to sh -c. To prevent bash from interpreting the string you are giving to sh -c, simply put it in ' instead of ":
sudo sh -c 'FOO=bar; echo Result:${FOO}'
1 Refer to the comments for reason.

This doesn't work, because the variable FOO is set for the following command, echo, but the ${FOO} is replaced by the enclosing shell.
If you want it to work, you must set the variable FOO for the enclosing shell and wrap the echo ... in single quotes
sudo FOO=bar sh -c 'echo Result:${FOO}'

Related

setting variable from Multiple sub variables bash [duplicate]

foo='abc'
bar='xyz'
var=bar
How to I get access to 'xyz' when I have var?
I've tried:
$(echo $var)
and
$(eval echo $var)
I just get bar: command not found
Use bash indirect variable reference:
${!var}
And of course can be done with eval, not recommended:
eval 'echo $'"$var"
Why:
$ bar=xyz
$ var='bar;whoami'
$ eval 'echo $'"$var"
xyz
spamegg
Th command whoami is being evaluated too as part of evaluation by eval, imagine a destructive command instead of whoami.
Example:
$ bar='xyz'
$ var=bar
$ echo "${!var}"
xyz
$ eval 'echo $'"$var"
xyz

Using /bin/bash -l -c with concatenated commands does not pass environment variables

I'm trying to execute a series of bash commands using /bin/bash -l -c as follows:
/bin/bash -l -c "cmd1 && cmd2 && cmd3..."
What I notice is that if cmd1 happens to export an environment variable, it is not seen by cmd2 and so on. If I run the same concatenated commands without the /bin/bash -l -c option, it just runs fine.
For example, this does not print the value of MYVAR:
$/bin/bash -l -c "export MYVAR="myvar" && echo $MYVAR"
whereas, the following works as expected
$export MYVAR="myvar" && echo $MYVAR
myvar
Can't find why using /bin/bash -l -c won't work here? Or are there any suggestion how to make this work with using /bin/bash -l -c?
variables are exported for child processes, parent process environment can't be changed by child process so consecutive commands (forking sub processes can't see variables exported from preceeding command), some commands are particular becuase don't fork new process but are run inside current shell process they are builtin commands only builtin commands can change current process environment export is a builtin, for more information about builtins man bash /^shell builtin.
Otherwise expansion is done during command parsing:
/bin/bash -l -c "export MYVAR="myvar" && echo $MYVAR"
is one command (note the nested quotes are not doing what you expect because are closing and opening quoted string, myvar is not quoted and may be split). So $MYVAR is expanded to current value before assigned to myvar.
export MYVAR="myvar" && echo $MYVAR
are two commands and $MYVAR because && is bash syntax (not literal like "&&"), is expanded after being assigned.
so that $ have a literal meaning (not expanded) use single quotes or escape with backslash.
/bin/bash -l -c 'export MYVAR="myvar" && echo "$MYVAR"'
or
/bin/bash -l -c "export MYVAR=\"myvar\" && echo \"\$MYVAR\""

Pass all args to a command called in a new shell using bash -c

I've simplified my example to the following:
file1.sh:
#!/bin/bash
bash -c "./file2.sh $#"
file2.sh:
#!/bin/bash
echo "first $1"
echo "second $2"
I expect that if I call ./file1.sh a b to get:
first a
second b
but instead I get:
first a
second
In other words, my later arguments after the first one are not getting passed through to the command that I'm executing inside a new bash shell. I've tried many variations of removing and moving around the quotation marks in the file1.sh file, but haven't got this to work.
Why is this happening, and how do I get the behavior I want?
(UPDATE - I realize it seems pointless that I'm calling bash -c in this example, my actual file1.sh is a proxy script for a command that gets called locally to run in a docker container so it's actually docker exec -i mycontainer bash -c '')
Change file1.sh to this with different quoting:
#!/bin/bash
bash -c './file2.sh "$#"' - "$#"
- "$#" is passing hyphen to populate $0 and $# is being passed in to populate all other positional parameters in bash -c command line.
You can also make it:
bash -c './file2.sh "$#"' "$0" "$#"
However there is no real need to use bash -c here and you can just use:
./file2.sh "$#"

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"

Inline bash script variables

Admittedly, I'm a bash neophyte. I always want to reach for Python for my shell scripting purposes. However, I'm trying to push myself to learn some bash. I'm curious why the following code doesn't work.
sh -c "F=\"123\"; echo $F"
It doesn't work because variable expansion in the double-quoted string happens before the command is called. That is, if I type:
echo "$HOME"
The shell transforms this into:
echo "/home/lars"
Before actually calling the echo command. Similarly, if you type:
sh -c "F=\"123\"; echo $F"
This gets transformed into:
sh -c "F=\"123\"; echo"
Before calling a the sh command. You can use single quotes to inhibit variable expansion, for example:
sh -c 'F="123"; echo $F'
You can also escape the $ with a backslash:
sh -c "F=\"123\"; echo \$F"
Not an answer to the core question, but if anyone is looking to do this inline in a (subjectively) more elegant way than bash -c:
( export MY_FLAG="Hello"; echo "$MY_FLAG" )
The syntax is nicer, no escape chars etc.

Resources