I am trying to get the output of a pipe into a variable. I tried the following things.

echo foo | myvar=$(</dev/stdin)
echo foo | myvar=$(cat)
echo foo | myvar=$(tee)

But $myvar is empty.

I don’t want to do.

myvar=$(echo foo)

Because I don’t want to spawn a subshell.

Any ideas?

Edit: I don’t want to spawn a subshell because the command before the pipe needs to edit global variables, which it can’t do in a subshell. Can it? The echo thing is just for simplification. It’s more like.

complex_function | myvar=$(</dev/stdin)

And I don’t get, why that doesn’t work. This works for example:

complex_function | echo $(</dev/stdin)
Best Answer


The correct solution is to use command substitution like this.

variable=$(complex_command)

as in

message=$(echo 'hello')

(or for that matter, message=hello in this case).

Your pipeline.

echo 'hello' | message=$(</dev/stdin)

or

echo 'hello' | read message

actually works. The only problem is that the shell that you're using will run the second part of the pipeline in a subshell. This subshell is destroyed when the pipeline exits, so the value of $message is not retained in shell.

Here you can see that it works.

$ echo 'hello' | { read message; echo "$message"; }
hello

... but since the subshell's environment is separate (and gone).

$ echo "$message"

(no output)

One solution for you would be to switch to ksh93 which is smarter about this.

$ echo 'hello' | read message
$ echo "$message"
hello

Another solution for bash would be to set the lastpipe shell option. This would mean the last part of the pipeline would run in the current environment This however does not work in interactive shells as lastpipe requires that job control is not active.

#!/bin/bash

shopt -s lastpipe
echo 'hello' | read message
echo "$message"