22

For simplicity I would like to do:

echo cart | assign spo;
echo $spo  

Output: cart

Does such an assign application exist?

I am aware of all the ways to do this using substitution.

Tegra Detra
  • 5,016
  • Why do you want to do this without substitution? – Thanatos Nov 08 '13 at 19:08
  • I like the reverse Polish notation flow when writing with pipes only. I am much faster coding and the codes quality / speed is not so important. Also I don't when chaining it is hella easier to comment out parts of the chain and echo the current output rather then erase ticks etc. – Tegra Detra Nov 08 '13 at 19:51
  • If you're concerned about ease of debugging, consider putting the backticks command on a series of separate lines. A=$( some | command | here ) with each of some |, command |, and here on its own line. – Chris Davies Jan 26 '15 at 22:59

6 Answers6

18

You could do something like this, if you use bash:

echo cart | while read spo; do echo $spo; done

Unfortunately, variable "spo" won't exist outside of the while-do-done loop. If you can get what you want done inside the while-loop, that will work.

You can actually do almost exactly what you wrote above in ATT ksh (not in pdksh or mksh) or in the fabulous zsh:

% echo cart | read spo
% echo $spo
cart

So, another solution would be to use ksh or zsh.

  • 1
    You can use coprocesses in the Korn Shell (e.g. mksh): echo cart |& while read -p spo; do echo $spo; done (actually better to use while IFS= read -p -r spo; do… though) – mirabilos Feb 27 '14 at 14:03
13
echo cart | { IFS= read -r spo; printf '%s\n' "$spo"; }

Would work (store the output of echo without the trailing newline character into the spo variable) as long as echo outputs only one line.

You could always do:

assign() {
  eval "$1=\$(cat; echo .); $1=\${$1%.}"
}
assign spo < <(echo cart)

The following solutions would work in bash scripts, but not at the bash prompt:

shopt -s lastpipe
echo cat | assign spo

Or:

shopt -s lastpipe
whatever | IFS= read -rd '' spo

To store the output of whatever up to the first NUL characters (bash variables can't store NUL characters anyway) in $spo.

Or:

shopt -s lastpipe
whatever | readarray -t spo

to store the output of whatever in the $spo array (one line per array element).

3

If I understand the issue correctly you want to pipe stdout to a variable. At least that was what I was looking for and ended up here. So for those who share my fate:

spa=$(echo cart)

Assigns cart to the variable $spa.

chaos
  • 48,171
zack-vii
  • 31
  • 2
  • 2
    That's what they call substitution, which the OP wanted to avoid. – Dmitry Grigoryev Sep 30 '15 at 20:20
  • This was useful to me, i just needed to assign it to a variable, i didn't care how it got there! – Chris Marisic Apr 24 '19 at 19:37
  • The shell does indeed pipe the output of the command to the variable. It ALSO pipes stdin into the command. This can mess up while loops that read input, and cause them to exhaust stdin and quit reading after one loop. This is a baffling situation -- no error occurs and no indication of what is the matter is exported.

    You must explicitly redirect stdin from /dev/null to prevent the loop from stopping at one iteration.

    out=$( prog </dev/null arg1 arg2 ...) # does the trick.

    Just putting this here to be stumbled upon someday and make someone's day.

    – Chris Reid Jan 04 '20 at 06:30
1

Here's my solution to the problem.

# assign will take last line of stdout and create an environment variable from it
# notes: a.) we avoid functions so that we can write to the current environment
#        b.) aliases don't take arguments, but we write this so that the "argument" appears
#            behind the alias, making it appear as though it is taking one, which in turn
#            becomes an actual argument into the temporary script T2.
# example: echo hello world | assign x && echo %x outputs "hello world"
alias assign="tail -1|tee _T1>/dev/null&&printf \"export \\\$1=\$(cat _T1)\nrm _T*\">_T2&&. _T2"
1

If you just want to output the current pipe stream use cat.

echo cart | cat 

If you want to continue your command chain, try using the tee command to echo the output.

echo cart | tee /dev/tty | xargs ls

You could use an alias to shorten the command.

alias tout='tee /dev/tty'
echo cart | tout | xargs ls
BillThor
  • 8,965
  • Why would you want to pipe output to a plain cat? – Chris Davies Jan 26 '15 at 22:57
  • 1
    @roaima Some command recognize they are running on a terminal an behave differently than if they are outputting to a pipe. In particular they may truncate lines to the current screen width. For most command the cat command is redundant. – BillThor Jan 27 '15 at 00:32
  • Ah ok, I see now what you were trying to illustrate. The echo | cat construct threw me. – Chris Davies Jan 27 '15 at 00:35
0

If you are willing to use a temporary file you can avoid substitution and the expensive pipe.

echo cart > tmp.$$; read spo < tmp.$$; rm tmp.$$

Fun shell fact: you can even move the redirection operator to the front for improved "pipe" feeling:

echo cart > tmp.$$; < tmp.$$ read spo
Jens
  • 1,752
  • 4
  • 18
  • 36