2

I am having the below shell script

var="this is a test"

ls -ltr| while read file
do
     echo $var
done
echo $var

I am getting the below output:

this is a test
this is a test
this is a test

How am I getting the value of the variable "var" set to "this is a test" inside while loop since piping will spawn a new sub-shell and I am also not exporting my "var" variable in the main shell?

As far as I know, in order for the child to inherit the variable values from the parent shell, we need to export the variable, but in this case the variable value is getting inherited without the "export" statement.

marked as duplicate by Jeff Schaller, Stephen Rauch, G-Man, Michael Homer, mdpc Dec 14 '17 at 1:31

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • In particular, jsbillings' answer – Jeff Schaller Dec 13 '17 at 20:01
  • 1
    subshells get copies of all variables, not only the exported ones, but that really has nothing to do with getting variables out of the different kinds of while structures, which is what that other Q is really about – ilkkachu Dec 13 '17 at 21:56
  • @ilkkachu "subshells get copies of all variables". I am not agree. Subshell get only environment variables and for putting the ordinary shell variable to the environment, we need the export command. – MiniMax Dec 13 '17 at 22:46
  • 1
    @MiniMax, subshells do. normal commands don't. – ilkkachu Dec 13 '17 at 22:56
  • 1
    @MiniMax: No, as this question shows, the while loop gets a copy of the local (non-exported) var variable, which is defined before the pipeline.  Similar thing would happen if the left hand side of the pipe was something like if true; then echo "$var"; var=quick; echo "The $var brown fox"; fi. – G-Man Dec 13 '17 at 23:38
3

Let's see what the Bash manual says (3.7.3 Command Execution Environment)

The shell has an execution environment, which consists of the following:

  • shell parameters that are set by variable assignment or with set or inherited from the shell’s parent in the environment
  • shell functions defined during execution or inherited from the shell’s parent in the environment

Command substitution, commands grouped with parentheses, and asynchronous commands are invoked in a subshell environment that is a duplicate of the shell environment [...]

Changes made to the subshell environment cannot affect the shell’s execution environment.

In 3.2.2 Pipelines, it's also said

Each command in a pipeline is executed in its own subshell

(Of course that only applies to multi-command pipelines)

So, the parts of a pipeline, as well as other subshells, get copies of all the shell variables, but any changes to them are not visible to the outside shell.

With other commands, you still need to export:

When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following.

  • shell variables and functions marked for export, along with variables exported for the command, passed in the environment

Food for thought: what does this print?

bash -c 'f() { echo "$a $b"; }; a=1; b=1; (b=2; f); f'
  • Well done! I have grasped just now, that my version can't be true, because the shell should left all variable names unsubstituted for the while loop, else how the while loop will do parameter expansion of inner variables in the each iteration? I will delete my answer - it's wrong. – MiniMax Dec 14 '17 at 0:37

Not the answer you're looking for? Browse other questions tagged or ask your own question.