33

I run the following script:

VAR="Test"
sh -c 'echo "Hello $VAR"'

But I get:

# ./test.sh
Hello

How can I send the variable VAR of my script to the shell created with sh -c '...'?

1
  • Could you not expand it before the call?: change the quotes sh -c "echo \"Hello $VAR\"". Sep 2, 2022 at 20:55

6 Answers 6

49

Either use export to turn it into an environment variable, or pass it directly to the command.

VAR="Test" sh -c 'echo "Hello $VAR"'

VAR="Test"
export VAR
sh -c 'echo "Hello $VAR"'

Avoid using double quotes around the shell code to allow interpolation as that introduces command injection vulnerabilities like in:

sh -c " echo 'Hello $VAR' "

causing a reboot if called when $VAR contains something like ';reboot #

5
  • Not really practical (I have several variables, and I don't want them to be environment variables), but it works, thanks! Oct 24, 2011 at 8:41
  • 4
    @Matthieu: They are only set as environment variables for the children of your process, if that's what worries you. Oct 24, 2011 at 12:47
  • 5
    Just FYI, You can also do export var="Test" in one line.
    – user606723
    Oct 24, 2011 at 14:16
  • @Piskvor well thank you for the precision, that's perfect then. Oct 25, 2011 at 7:50
  • if the variable is intended to be used by the child process only (ie, not the current context), then don't export and follow @Stéphane Chazelas advice; if you need more than one declaration/definition: VAR1=aaa VAR2=bbb sh -c ... Dec 11, 2020 at 17:36
11

Here's yet another way to pass variables to sh -c (as positional arguments):

{
VAR="world"
VAR2='!'
sh -c 'echo "Hello ${0}${1}"' "$VAR" "$VAR2"
}
2
  • 1
    (+1) To keep it more in line with the normal $1 $2 expectation for script variables, it can have a dummy value for $0. This will allow $@ to work as expected, eg. sh -c 'echo "Hello $@"' _ "$VAR" "$VAR2" `
    – Peter.O
    Oct 24, 2011 at 16:09
  • 2
    @Peter.O Rather than using "_", I would use "sh" or a sensible name to give to that command, since that $0 is displayed in the error/warning messages by the shell. Jan 26, 2013 at 21:45
6

If you don't want to export them as environment variables, here's a trick you could do. Save your variabe definition to a file .var_init.sh and source it in your sub-shell like this:

.var_init.sh

VAR="Test"

from the command line:

sh -c ". .var_init.sh && echo \$VAR" # Make sure to properly escape the '$'

This way, you only set your variables at the execution of your subshell.

2
  • ... or ENV=.var_ini.sh sh -c '...'
    – Kusalananda
    May 7, 2018 at 9:47
  • Note that if that .var_init.sh is expected to be looked for in the current directory (as opposed to $PATH), it should be written . ./var_init.sh Aug 31, 2019 at 12:58
1

If you're using sudo sh -c, the environment variables are not passed along, but you can get around it by using the --preserve-env argument:

export VAR="test for exporting"
sudo --preserve-env=VAR sh -c 'echo "This is a $VAR"'

This passes along just the variables you need rather than using sudo -E which passes along your entire set of environment variables. From Sudo Manual:

--preserve-env=list Indicates to the security policy that the user wishes to add the comma-separated list of environment variables to those preserved from the user's environment. The security policy may return an error if the user does not have permission to preserve the environment.

Or, you could assign it directly as:

export VAR="test for exporting"
sudo VAR1="$VAR" sh -c 'echo "This is a $VAR1"'
0

Thanks to "enharmonic" post, I've solved my problem. I had to change this command that adds the "/etc/docker/daemon.json" file if not already present:

sudo echo "{ ${dqt}insecure-registries${dqt} : [${dqt}$ipaddress:5000${dqt}] }" >> $dockdaemoncfg

this command must be changed because it is not able to keep the content of both the variables "$ipaddress" and "${dqp}" (where is the double quotes dqt='"') and wrote nothing in their place into the file. So thanks to "enharmonic" solution I successfully changed that as follows:

sudo VAR1="$IPaddress" VAR2="$dockdaemoncfg" bash -c 'echo "{ \"insecure-registries\" : [\"$VAR1:5000\"] }" >> "$VAR2"'
0

How about a subshell instead?

When I used to use inline scripts often it was because I wasn't aware of how to use subshells.

I just found the answer for how to pass variables to a subshell, but this showed up in my search results while I was doing so, so I'll post that here as well.

Passing Variables to a Subshell

I'm showing how to do this for a Posix Shell, which includes bash, zsh, most Docker shells and, well, just about every shell you'll come across on Unix-y systems.

If you wrap a subshell in a function You can pass arguments to the function and those arguments will be locally scoped to its subshell.

For reference

  • ( ) is a subshell
  • $( ) is a subshell which captures text
  • my_name() { } is a function

Simple Example

my_subsh_func() { (
  echo "${1}"
); }

my_subsh_func 'Hello, World!'

Complete Script Example

#!/bin/sh
set -e
set -u

my_foo="foo"

subshell_function() { (
  echo "my_foo='${my_foo}'"
  echo "my_\$1='${1}'"
); }

my_var='Hello, World!'

subshell_function "${my_var}"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .