(You most likely have firstline
set already when you tested that code in bash
, its value should be empty at the end).
When running the pipeline
cat myfile | { read firstline; read secondline; }
the right hand side is running in a subshell. Not because of the { ...; }
but because it's part of a pipeline. The subshell environment will contain the two variables firstline
and secondline
(after both have been read), but will be destroyed when the subshell terminates, discarding both variables.
This holds true for both POSIX sh
and bash
.
In bash
(4.2+), you can work around this by setting the lastpipe
shell option. From the bash
manual:
Each command in a pipeline is executed as a separate process (i.e., in
a subshell). See COMMAND EXECUTION ENVIRONMENT for a description of a
subshell environment. If the lastpipe
option is enabled using the
shopt
builtin (see the description of shopt
below), the last element of
a pipeline may be run by the shell process.
This would work in a script, but not in an interactive shell with job control enabled (it's the job control that makes it not work, not the interactivity).
Example:
$ cat script.sh
cat << EOF > myfile
line1
line2
EOF
cat myfile | { read firstline; read secondline; }
printf 'first=%s\n' "$firstline"
printf 'second=%s\n' "$secondline"
shopt -s lastpipe
cat myfile | { read firstline; read secondline; }
printf 'first=%s\n' "$firstline"
printf 'second=%s\n' "$secondline"
$ bash script.sh
first=
second=
first=line1
second=line2
In your particular case, you could also, in both POSIX sh
and bash
, do away with cat
and the pipe completely and instead redirect into the compound command with the two read
calls directly:
{ read firstline; read secondline; } <myfile
On a tangential note, you most likely do not have a real historical Bourne shell on your machine (unless it's a Solaris system before Solaris 11). I'm assuming you mean a modern POSIX sh
shell.
$firstline
should be empty inbash
as well at the end. Did you setfirstline
before running this code? Unset it withunset firstline
and try again.{...}
in bash, unless you have theshopt -s lastpipe
set. Posix shells also do not print that value.