8

With bash 4.1.2 and 4.3.48, the following script gives the expected output:

#!/bin/bash

returnSimple() {
   local  __resultvar=$1
   printf -v "$__resultvar" '%s' "ERROR"
   echo "Hello World"
}

returnSimple theResult
echo ${theResult}
echo Done.

Output as expected:

$ ./returnSimple
Hello World
ERROR
Done.

However, when stdout from the function is piped to another process, the assignment of the __resultvar variable does not work anymore:

#!/bin/bash

returnSimple() {
   local  __resultvar=$1
   printf -v "$__resultvar" '%s' "ERROR"
   echo "Hello World"
}

returnSimple theResult | cat
echo ${theResult}
echo Done.

Unexpected Output:

$ ./returnSimple
Hello World

Done.

Why does printf -v not work in the second case? Should printf -v not write the value into the result variable independent of whether the output of the function is piped to another process?

2
  • A pipe is distinct from IO redirection. Commented Oct 12, 2017 at 13:43
  • @chepner Right, and that is also the reason why I observed that this use case does work with redirection, but not with piping ... fixed that detail in the question Commented Oct 12, 2017 at 13:45

2 Answers 2

8

See man bash, section on Pipelines:

Each command in a pipeline is executed as a separate process (i.e., in a subshell).

That's why when you write cmd | cat, cmd receives a copy of variable that it can't modify.

A simple demo:

$ test() ((a++))
$ echo $a

$ test
$ echo $a
1
$ test | cat
$ echo $a
1
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that is exactly what I missed ... I just also found that out ;-)
2

Interestingly enough, the same also happens when using eval $__resultvar="'ERROR'" instead of the printf -v statement. Thus, this is not a printf related issue.

Instead, adding a echo $BASH_SUBSHELL to both the main script and the function shows that the shell spawns a sub shell in the second case - since it needs to pipe the output from the function to another process. Hence the function runs in a sub shell:

#!/bin/bash

returnSimple() {
    local  __resultvar=$1
    echo "Sub shell level: $BASH_SUBSHELL"
    printf -v "$__resultvar" '%s' "ERROR"
}

echo "Sub shell level: $BASH_SUBSHELL"
returnSimple theResult | cat
echo ${theResult}
echo Done.

Output:

% ./returnSimple.sh
Sub shell level: 0
Sub shell level: 1

Done.

This is the reason why any variable assignments from within the function are not passed back to the calling script.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.