21

I know that I can run a command with an environment variable like this:

FOO=bar mycommand

I know that I can run commands in a subshell like this:

(firstcommand && secondcommand)

But can I somehow combine those two?

FOO=bar (firstcommand && secondcommand)

gives:

sh: syntax error: unexpected "("

at least in busybox shell (ash).

Edit: Kusalananda suggested FOO=bar sh -c 'first && second' which is indeed a solution. However, I am also interested in alternative answers because I like the subshell syntax because it doesn't require fiddling around with escaping of quotes.

0

5 Answers 5

26

One way:

FOO=bar sh -c 'first && second'

This sets the FOO environment variable for the single sh command.

To set multiple environment variables:

FOO=bar BAZ=quux sh -c 'first && second'

Another way to do this is to create the variable and export it inside a subshell. Doing the export inside the subshell ensures that the outer shell does not get the variable in its environment:

( export FOO=bar; first && second )

Summarizing the (now deleted) comments: The export is needed to create an environment variable (as opposed to a shell variable). The thing with environment variables is that they get inherited by child processes. If first and second are external utilities (or scripts) that look at their environment, they would not see the FOO variable without the export.

0
0

Use the && operator to join your subshell declaration to the environment variable assignment statement:

FOO=bar && (firstcommand && secondcommand)

e.g.:

GREETING='Hello' && ( echo "${GREETING}, friend!" )
# Hello, friend!

&& also overcomes a related problem in which a command will use the prior value of a variable if that variable is redefined with a new value as a prefix to the command:

GREETING='Halt'
GREETING='Hello' && echo "${GREETING}, friend!"
# Hello, friend!

Without &&:

GREETING='Halt'
GREETING='Hello' echo "${GREETING}, friend!"
# Halt, friend!
# OOPS!
2
  • Your GREETING='Halt' // GREETING='Hello' echo "${GREETING}, friend!" should be expected behaviour. The variable $GREETING is evaluated/expanded before the command runs, so it has the value Halt. Then the command is run, GREETING='Hello' && echo "Halt, friend!" but there's nothing now using $GREETING Jun 26 at 15:42
  • @roaima yes, thanks for expanding upon why the last example works the way it does. Jun 28 at 1:34
0

You could put the declaration inside the subshell:

( export FOO=bar; firstcommand && secondcommand )

or POSIXly,

( export FOO; FOO=bar; firstcommand && secondcommand )
-1

The environment of the subshell has a lot in common with the parent. Let's look at the definition first. subshell is a child process launched by a shell (when we are in front of a shell prompt) or a shell script.

In shell programming (bash in particular), "( )" starts a subshell. Whatever variables you define and assign inside the subshell is not visible to the parent process. This is usually the source of some shell programming errors.

A function in bash is essentially a command and a subshell but with the side effect of all its varables are also GLOBAL unless you add the LOCAL qualifier. The variable outside the function is visible inside the function. Thus your subshell's environment is essentially the global variable in the shell script.

As a small example in Bourne shell:

#!/bin/sh

a_function() {
    echo "function is essentially a subshell"
    echo "inside a_function"
    echo "it can see parent variables: shellvar=$shellvar"
}

shellvar="shellvariable is set from parent shell"
echo "shellvar from parent: $shellvar "
a_function

If you run this program:

./test.sh
shellvar from parent: shellvariable is set from parent shell 
function is essentially a subshell
inside a_function
it can see parent variables: shellvar=shellvariable is set from parent shell

To fully understand processes, child processes, subshells, you may want to read about fork, exec. Note the export somevar=somevalue is making somevar visible to all subsequent processes, not just the subshell. Process is organized like a tree.

3
  • 1
    You can't really compare a function with a subshell, as setting a variable in a function will make it available in the calling environment whereas setting a variable in a subshell would not.
    – Kusalananda
    Mar 23, 2019 at 21:32
  • Yes, your are right. Only when you use local inside the function then you can hide it. This is the tricky part of shell programing, inside function you can trump some global variable. I tried to stick to the principle of adding local to variable inside function. I think this feature is only available in bash not in Bourne.
    – Kemin Zhou
    Mar 24, 2019 at 1:50
  • You could write the function with func () (...) instead of with func () {...} which would make it run in a subshell... But that would be because of the actual subshell and not because it was a function.
    – Kusalananda
    Mar 24, 2019 at 7:06
-3

For general export, add this line in top makefile

SHELL:=NODE_PATH=. ${SHELL}

where "NODE_PATH=." is an example var

I use this in this example project:

https://github.com/rzr/webthing-iotjs/blob/sandbox/rzr/node/master/Makefile#L24

You must log in to answer this question.

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