Bash propagate variables when called with -c - bash

I'm using ZSH.
Why does the last command not output anything?
$ cat create_foo.sh
foo=bar
$ source create_foo.sh && echo $foo
bar
$ unset foo
$ bash -c "source create_foo.sh && echo $foo"
From the last command I don't get any output.

Because $foo is getting substituted with an empty string in the double quotes.
You need to either use single quotes or escape the $ sign:
$ bash -c 'source create_foo.sh && echo $foo'
bar
$ bash -c "source create_foo.sh && echo \$foo"
bar

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

Is there a way to print interpolated shell commands while preserving redirections?

Suppose I have the following shell program.
#!/bin/sh
FOO="foo"
echo $FOO | cat
I want to generate another shell program that does the same thing as this one, except that all shell variables have been substituted. For example,
#!/bin/sh
echo "foo" | cat
I know that I can get close if I run the above program using #!/bin/sh -x, but that output does not preserve redirections. Instead, I get
+ FOO=foo
+ echo foo
+ cat
foo
Any ideas?
The following shell:
$ cat eval.sh
echo "#!/bin/sh"
FOO="foo"
echo "echo $FOO | cat"
will write a shell:
$ sh eval.sh
#!/bin/sh
echo foo | cat
which does what you need.

macOS Sierra: ${TAIL} is not working in zsh

I was trying to execute some bash scripts in zsh (oh-my-zsh). I found ${TAIL} is not working in zsh.
bash:
bash-3.2$ ${CD} /tmp; echo "test" >> test.txt; ${TAIL} test.txt
bash: /tmp: is a directory
test
zsh:
~ ${CD} /tmp; echo "test" >> test.txt; ${TAIL} test.txt
zsh: command not found: tail -f
✘ /tmp
But using tail directly is fine
✘ /tmp tail -f test.txt
test
test
whereis tail
/usr/bin/tail
echo $PATH
/usr/local/bin:/usr/bin
I think this is a classic case in zsh for Why does $var where var="foo bar" not do what I expect?
Unlike bash, by default, zsh does not split into words when passed to a command or used in a loop as for foo in $var.
var="foo bar"
enabled the flag manually as
setopt shwordsplit
then try the same as
echo "test" >> test.txt; ${TAIL} test.txt

Shell script with sudo sh

I want to make a script to install a program (ROS) and I need to write this line:
sudo sh -c 'echo "TEXT VARIABLE TEXT" > systemFile' # to write in systemaFile I need sudo sh
if echo is just fixed text, it works.
If echo is text + variable it doesn't work.
I've tried with:
read f1 < <(lsb_release -a | grep Code* | cut -f2) #codename is writted in variable $f1
echo $f1 # retruns "quantal" as I expected
sudo sh -c 'echo "TEXT $f1 TEXT" > systemFile' #f1 is empty, WHY?
Then I have to assign the variable inside the same instruction sudo sh, for example:
sudo sh -c ' read f1 < <(lsb_release -a | grep Code* | cut -f2) ; echo "TEXT $f1 TEXT" > systemFile'
sh: 1: Syntax error: redirection unexpected
Please try just like this script line
sudo sh -c 'echo TEXT '$f1' TEXT > systemFile'
sudo bash -c 'echo TEXT '$f1' TEXT > systemFile'
i have use this able script line in .sh file and its working fine.
This can work too:
sudo sh -c "echo 'TEXT $VARIABLE TEXT' > systemFile"
However, it is generally not recommended to un-necessarily run a command as sudo. You seem to want only redirection to be "sudoed". So try these options:
echo "TEXT $VARIABLE TEXT" | sudo tee systemFile >/dev/null
echo "TEXT $VARIABLE TEXT" | sudo dd of=systemFile
echo can be simple echo, or any other command you want. Note that this command is not being run under sudo.
use -E option to run sudo:
sudo -E sh -c 'echo "TEXT VARIABLE TEXT" > systemFile'
from man sudo:
-E
The -E (preserve environment) option indicates to the security policy that the user wishes to preserve their existing environment variables. The security policy may return an error if the -E option is specified and the user does not have permission to preserve the environment.
Shell variables are only expanded in "double quotes", not in 'single quotes'.
$ v=value
$ echo $v
value
$ echo "$v"
value
$ echo '$v'
$v
You're starting a new instance of sh which then runs the command echo "TEXT $f1 TEXT" > systemFile.
Since $f1 has not been assigned within the new process, it's empty.
To fix this, you can expand $f1 and pass it in the command line:
sudo sh -c 'echo "TEXT '$f1' TEXT" > systemFile'
Or export it so it's available to the child process (using -E to preserve the environment, thanks anishsane):
export f1
sudo -E sh -c 'echo "TEXT $f1 TEXT" > systemFile'

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

$ 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}'

Resources