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.