0

The code

In the example function a, I capture the input from a pipe as follows:

function a() { 
    if [ -t 1 ]; then 
        read test
        echo "$test"
    fi

    if [[ -z "$1" ]]; then 
        echo "$1"
    fi
}

called as follows:

echo "hey " | a "hello"

produces the output:

hey
hello

The issue

I was inspired by this answer, however a quote after the snippet has me concerned:

But there's no point - your variable assignments may not last! A pipeline may spawn a subshell, where the environment is inherited by value, not by reference. This is why read doesn't bother with input from a pipe - it's undefined.

I'm not sure I understand this - attempting to create subshells yielded the output I expected:

function a() { 
    (
        if [ -t 1 ]; then 
            read test
            echo "$test"
        fi

        if [[ -z "$1" ]]; then 
            echo "$1"
        fi
    )
}

And in the method call:

(echo "hey") | (a "hello")

still yields:

hey
hello

So what is meant by your variable assignments may not last! A pipeline may spawn a subshell, where the environment is inherited by value, not by reference.? Is there something that I've misunderstood?

Community
  • 1
  • 1
Nick Bull
  • 9,518
  • 6
  • 36
  • 58

2 Answers2

2

The quoted note is incorrect. read doesn't care where its input comes from.

However, you must remember that the variable assigned to by the invocation of the read command is part of the (sub-)shell which executes the command.

By default, each command executed in a pipeline (a series of commands separated by |) is executed in a separate subshell. So after you execute echo foo | read foo, you will find that the value of $foo has not changed: not because read ignored its input but rather because the shell read executed in no longer exists.

rici
  • 234,347
  • 28
  • 237
  • 341
1

Try this:

echo test | read myvar
echo $myvar

You might expect that it will print test, but it doesn't, it prints nothing. The reason is that bash will execute the read myvar in a subshell process. The variable will be read, but only in that subshell. So in the original shell the variable will never be set.

On the other hand, if you do this:

echo test | { read myvar; echo $myvar; }

or this

echo test | (read myvar; echo $myvar)

you will get the expected output. This is what happens with your code.

redneb
  • 21,794
  • 6
  • 42
  • 54
  • Awesome to know - thanks! I think that you should have noted that this means that the quote is wrong (as the variable is assigned, so long as the original subshell is retained using code blocks, `{}` or `()`, and not because *variable assignments may not last* because it *may spawn a subshell*). – Nick Bull Sep 17 '16 at 21:42