Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grouping commands in curly braces and piping does not preserve variable

Tags:

bash

Say I have a file myfile in my current working directory. I want to set a variable if a command executes normally, but also use its result.

$ ls myfile && v=3
myfile
$ echo "$v"
3

But now I also want to pipe the result, so I use the { list; } syntax to group the commands:

$ unset v
$ { ls myfile && v=3; } | grep myf
myfile
$ echo "$v"
                  # v is not set

Bash reference manual -> 3.2.4.3 Grouping Commands says:

{ list; }

Placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created. The semicolon (or newline) following list is required.

So, to my understanding, v should be set to 3. But it is not happening. Why?

like image 604
fedorqui 'SO stop harming' Avatar asked Jun 09 '15 09:06

fedorqui 'SO stop harming'


2 Answers

It's not the curly braces that are causing a subshell to be created, it's the pipe.

To prove it:

$ { ls && v=3; } > tmp
$ echo "$v"
3

To quote Greg:

In most shells, each command of a pipeline is executed in a separate SubShell.

like image 179
Tom Fenech Avatar answered Oct 19 '22 18:10

Tom Fenech


You can use BASH_SUBSHELL variable to verify whether you're in subshell or not.

# BASH_SUBSHELL will print level of subshell from top due to pipe
{ unset v && ls file && v=3 && echo "$BASH_SUBSHELL - $v"; } | nl
     1  file
     2  1 - 3

# outside BASH_SUBSHELL will print 0
echo "$BASH_SUBSHELL - $v";
0 -

You can use for piped command it prints 1 meaning it is in a subshell hence value of v isn't available outside (evident from 2nd output)

like image 4
anubhava Avatar answered Oct 19 '22 19:10

anubhava