3

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.

MiniMax
  • 4,123
  • 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
  • 2
    @MiniMax, subshells do. normal commands don't. – ilkkachu Dec 13 '17 at 22:56
  • @ilkkachu But the each part of a pipeline is running in the own subshell. So, in this case, the ls -ltr and while runs each in the own subshell. Thus, they see only environment variables. – MiniMax Dec 13 '17 at 23:00
  • 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 Says 'Reinstate Monica' Dec 13 '17 at 23:38
  • @G-Man Why in this case this code works? - true | sed '' <<< $var. The sed is not builtin command, but it also prints the value of the $var. How it knows this value in the subshell? I think, that the expansion occurs in the parent shell and the sed get the value of the $var. I have elaborated this thought in the answer. – MiniMax Dec 13 '17 at 23:52
  • @BLayer Yes, I think the same :) – MiniMax Dec 14 '17 at 00:03
  • Had a related question a while back: https://askubuntu.com/q/704154/295286 Might be useful – Sergiy Kolodyazhnyy Dec 14 '17 at 01:19
  • @MiniMax:  If you understand now that you were confused,  you can also delete your comments that reflect that confusion. – G-Man Says 'Reinstate Monica' Dec 14 '17 at 02:18
  • @G-Man I think it is useful discussion. Someone can have same thoughts. Or you think it is confusing? – MiniMax Dec 14 '17 at 10:15

1 Answers1

6

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'
ilkkachu
  • 138,973
  • 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 00:37