In your example the combine
command will just be run as soon as the subshell exits (and provided the last background process was started without an error). The subshell will exit immediately after the jobs are started since there is no wait
command.
If you want to execute a command based on the return value of two or more simultaneous background processes, then I can't see any other way other than to use temporary files for the return values. This is because wait
can only return the return value of one of the processes it waits for. Also since the background processes must be run in subshells to get their return values at all, they cannnot be stored in variables. You could do:
something InputA >IrrelevantA &
something InputB >IrrelevantB &
tmp1=$(mktemp)
tmp2=$(mktemp)
( somethingElse InputA >OutputA; echo $? >"$tmp1" ) &
proc1=$!
( somethingElse InputB >OutputB; echo $? >"$tmp2" ) &
proc2=$!
wait "$proc1" "$proc2"
read ret1 <"$tmp1"
read ret2 <"$tmp2"
[ "$ret1" = 0 && "ret2" = 0 ] && combine OutputA OutputB >Result
rm "$tmp1" "$tmp2"
If you don't really care about the return values, you can just start the jobs normally and use wait
:
something InputA >IrrelevantA &
something InputB >IrrelevantB &
somethingElse InputA >OutputA &
proc1=$!
somethingElse InputB >OutputB &
proc2=$!
wait "$proc1" "$proc2"
combine OutputA OutputB >Result
As you wish
Nearly hit. The correct syntax is:
(command11; command12; command13) &
(command21; command22; command23) &
(command31; command32; command33) &
(command41; command42; command43) &
As you wish, but better
Or, if you want inside a group the commands to be left out after a command failed, then the syntax is
command11 && command12 && command13 &
command21 && command22 && command23 &
command31 && command32 && command33 &
command41 && command42 && command43 &
Note, "&&
" has a very different meaning as "&
". "&&
" means, that the command will run only after the previous is ready, and only if it was executed without a failure (i.e. its exit code is zero). "&
" means that the command before it will be run parallel with the main execution line, and its failure or success doesn't matter.
Using the xargs
tool
However, these solutions have the disadvantage, that you can't wait after all of these commands were executed. To do that, we have a little bit more trickery. Specially in your case, the required command to do this would be
for i in 1 2 3 4 5 6 7 8 9 10
do
echo "command${i}1 && command${i}2 && command${i}3"
done | xargs -P 10 -l 1 bash -c
It uses the very useful xargs
tool. Of course you can pipe anything into it, for example if you want to process thousands of things in 10 threads parallel, it will do.
It works on a way, that it calls the command bash -c
for all lines of input, parallel, but so, that always at most 10 child processes will coincidentally run (this is done by the -P 10
). The xargs
command end only if all of the bash processes were executed.
Parallel tool (see other answers)
The GNU has also written a tool named parallel
as well. As far I know, its syntax is a little bit more clear as of the xargs
s, although it has lesser features and it is not so common. Likely other answers will explain it.
NodeJs "parallel" module
If you are working in the nodejs framework, also there is a very commonly used tool (npm install parallel
), which is used mainly for parallelizing automatized build tasks, but it can be used also in shellscripts easily.
It is not a very good idea in common environments, because npm
packages don't integrate very well with the Unix packet-handling environment. Although its syntax are far more easy, its features are far behind all of the other solutions.
In the case of your specific problem, I would likely choose the second solution in a simple case, and the xargs-based solution in a more complex one.
Best Answer
The commands within each group run in parallel, and the groups run sequentially, each group of parallel commands waiting for the previous group to finish before starting execution.
The following is a working example:
Assume 3 groups of commands as in the code below. In each group the three commands are started in the background with
&
.The 3 commands will be started almost at the same time and run in parallel while the script
waits
for them to finish.After all three commands in the the third group exit,
command 10
will execute.