I'm wondering if there is any rule of the execution order of process substitution in Bash.
Let's assume 3 codes below. The only differences are redirections and output file names.
Bash version is 5.0.17
codes
# code1
echo 'a_ ' | tee \
>(sed 's/ /1/g' >> file-1) \
>(sed 's/ /2/g' >> file-1) \
>(sed 's/ /3/g' >> file-1) \
>/dev/null
# code2
echo 'a_ ' | tee \
>(sed 's/ /1/g' > file-2) \
>(sed 's/ /2/g' >> file-2) \
>(sed 's/ /3/g' >> file-2) \
>/dev/null
# code3
echo 'a_ ' | tee \
>(sed 's/ /1/g' > file-3) \
>(sed 's/ /2/g' > file-3) \
>(sed 's/ /3/g' > file-3) \
>/dev/null
result
head file*
==> file-1 <== a_3 a_2 a_1 ==> file-2 <== a_1 a_2 ==> file-3 <== a_1
From the result, the execution order of process substituion seems to be:
At code1, 3→2→1
At code2, 3→1→2
At code3, (2→3 or 3→2)→1
I would like to know the rule of the order.
Any help?
CodePudding user response:
if there is any rule of the execution order of process substitution in Bash
No, there is no rule. There is no synchronization.
Bash spawns (fork exec) processes. These processes are completely separate, with no synchronization between them.
There is "order of spawning" - the left side of pipeline has to be started before the right side. But that is not sequencing of execution, only sequencing of "starting".
echo 'a_ ' | tee \ >(sed 's/ /1/g' > file-3) \ >(sed 's/ /2/g' > file-3) \ >(sed 's/ /3/g' > file-3) \ >/dev/null
Let's try to draw a tree of processes. Note that each "spawn" in this tree starts a new concurrent unsequenced process.
- we start with our main bash process
- spawn subshell for the right side of the pipeline
- spawn subshell for the first >(...)
- open truncate file file-3
- spawn sed 's/ /1/g'
- spawn subshell for the second >(...)
- open truncate file file-3
- spawn sed 's/ /1/g'
- spawn subshell for the third >(...)
- open truncate file file-3
- spawn sed 's/ /1/g'
- open truncate /dev/null
- spawn tee
- wait for tee to finish
- wait for the third >(...) to finish execution
- wait for the second >(...) to finish execution
- wait for the first >(...) to finish execution
- spawn subshell for the first >(...)
- spawn subshell for the left side
- spawn echo 'a_ '
- wait for echo to finish
- wait for left side subshell to finish
- wait for right side subshell to finish
- spawn subshell for the right side of the pipeline
Each "branch" of the tree is executed concurrently, asynchronously. Only dots on the same level started from the same level happen sequentially. Moreover, parent processes are not synchronized with child processes - they only wait for the processes to finish the whole execution, not to finish a particular action.
Every single open truncate file file-3
is not sequenced with every other. One >(...)
may finish the whole execution, the other may not even start. There is no sequencing.