I'm trying to understand a difference between shell subprocess invocation using round brackets and curly brackets. I thought that curly brackets do not launch a subprocess, but it seems that they do launch a subprocess.
#!/bin/sh
a=1
b=1
( a=2; ) | ( a=3; )
{ b=2; } | { b=3; }
echo "a=$a"
echo "b=$b"
This script prints
a=1
b=1
So it seems that all invocations are run inside subprocesses. Is there any difference between them in that context? I understand that if I would use &&
and ||
, then {..}
will not launch a subprocess, but I'm trying to understand how pipes work.
To demonstrate that it's the pipeline itself that's generating the subshell, and that curly braces won't change this either way:
#!/bin/bash
echo "Base: $BASHPID"
( echo "In (): $BASHPID" ) # This will differ from the base
{ echo "In {}: $BASHPID"; } # This will match the base
# In bash, these will both differ from the base
echo "Pipeline, default config:"
{ echo " X: $BASHPID" >&2; } | { echo " Y: $BASHPID" >&2; }
# This is exactly the same without the {}s
echo "Pipeline, no {}s, default config:"
echo " X: $BASHPID" >&2 | echo " Y: $BASHPID" >&2
# Only the former will differ from the base if running a new enough bash
shopt -s lastpipe
echo "Pipeline, lastpipe enabled:"
{ echo " X: $BASHPID" >&2; } | { echo " Y: $BASHPID" >&2; }
Running this locally with bash 4.3, I get:
Base: 82811
In (): 82812
In {}: 82811
Pipeline, default config:
X: 82813
Y: 82814
Pipeline, no {}s, default config:
X: 82815
Y: 82816
Pipeline, lastpipe enabled:
Y: 82811
X: 82817
Note that since all pipeline components run simultaneously, there's no defined ordering of which of X
or Y
will emit output first; however, with lastpipe
enabled, the last pipeline component is invoked in a shell that's already up and running (doesn't need to be fork()
ed off from the main process), which slightly modifies the likelihood of who writes to stdout first.
{ ... }
doesn't spawn a sub-shell. What you're seeing is due to the fact you're using |
between 2 curly list commands.
It will be evident with this test:
$> b=1
$> echo $BASHPID
4401
$> { echo "X. $BASHPID"; b=2; } | { echo "Y. $BASHPID"; b=3; }
Y. 46902
$> echo $BASHPID
4401
$> declare -p b
declare -- b="1"
You can see that { echo "Y. $BASHPID"; b=3; }
gets executed in a different sub-shell hence changes made to b
are not reflected in current shell where b
is still 1
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With