With ksh I'm using read as a convenient way to separate values:
$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a
2 1
$
But it fails in Bash:
$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a
$
I didn't find a reason in the man page why it fails, any idea?
-
2This is discussed (somewhat obscurely) in page 024 of Greg’s Bash FAQ.Scott - Слава Україні– Scott - Слава Україні2014年07月11日 18:27:39 +00:00Commented Jul 11, 2014 at 18:27
-
Also similar: Why is my variable local in one 'while read' loop, but not in another seemingly similar loop?ilkkachu– ilkkachu2020年12月01日 20:52:47 +00:00Commented Dec 1, 2020 at 20:52
2 Answers 2
bash
runs the right-hand side of a pipeline in a subshell context, so changes to variables (which is what read
does) are not preserved — they die when the subshell does, at the end of the command.
Instead, you can use process substitution:
$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1
In this case, read
is running within our primary shell, and our output-producing command runs in the subshell. The <(...)
syntax creates a subshell and connects its output to a pipe, which we redirect into the input of read
with the ordinary <
operation. Because read
ran in our main shell the variables are set correctly.
As pointed out in a comment, if your goal is literally to split a string into variables somehow, you can use a here string:
read a b dump <<<"1 2 3 4 5"
I assume there's more to it than that, but this is a better option if there isn't.
-
3Or even
read a b dump <<< '1 2 3 4 5'
.choroba– choroba2014年07月11日 10:41:25 +00:00Commented Jul 11, 2014 at 10:41 -
Thank you to all, btw I noted that mksh (on cygwin) is doing the same as bash.Emmanuel– Emmanuel2014年07月11日 10:56:38 +00:00Commented Jul 11, 2014 at 10:56
-
@Michael Homer Good explanation! I found another one example that can explain that every command in pipeline run in own subshell:
cat /etc/passwd | (read -r line ; echo $line)
. But nextecho
of$line
which is not in pipeline put nothing on screen, because value was existed just between parentheses (subshell) . Hope, It helps someone.Yurij Goncharuk– Yurij Goncharuk2018年05月03日 10:28:28 +00:00Commented May 3, 2018 at 10:28
This is not a bash
bug as POSIX
allows both bash
and ksh
behaviors, leading to the unfortunate discrepancy you are observing.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12
Additionally, each command of a multi-command pipeline is in a subshell environment; as an extension, however, any or all commands in a pipeline may be executed in the current environment. All other commands shall be executed in the current shell environment.
However, with bash 4.2
and newer, you can set the lastpipe
option in non interactive scripts to get the expected result, eg:
#!/bin/bash
echo 1 2 3 4 5 | read a b dump
echo before: $b $a
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a
Output:
before:
after: 2 1
-
1+1 thank you for the "lastpipe" info. sorry for the delayEmmanuel– Emmanuel2014年07月29日 14:47:41 +00:00Commented Jul 29, 2014 at 14:47
-
1the problem with
lastpipe
is that it doesn't work in other shells (e.g. dash). there's basically no way to do this portable short of running everything in that subshell, see stackoverflow.com/questions/36268479/…anarcat– anarcat2017年02月08日 15:46:14 +00:00Commented Feb 8, 2017 at 15:46 -
2@anarcat This is correct but the question asked here was about bash.jlliagre– jlliagre2017年02月08日 15:49:17 +00:00Commented Feb 8, 2017 at 15:49
-
@anarcat: This may change in the future since there is a wish to change POSIX for another reason (PIPE Status) see here: unix.stackexchange.com/questions/476834/… Changing other shells is not trivial, it took me several months to rewrite the parser and interpeter on the Bourne Shell (bosh) to implement the faster behavior of the modern ksh.schily– schily2018年10月21日 13:42:59 +00:00Commented Oct 21, 2018 at 13:42