Collecting process ids of parallel process in bash

2019-07-10 10:44发布

Below I have a script that is collecting the process ids of individual commands, and appending them to an array in bash. For some reason as you can see stdout below, the end resulting array just contains one item, the latest id. How can the resulting PROCESS_IDS array at the end of this script contain all four process ids?

PROCESS_IDS=()

function append {
    echo $1
    PROCESS_IDS=("${PROCESS_IDS[@]}" $1)
}

sleep 1 && echo 'one' & append $! &
sleep 5 && echo 'two' & append $! &
sleep 1 && echo 'three' & append $! &
sleep 5 && echo 'four' & append $!
wait

echo "${PROCESS_IDS[@]}"

Here is the stdout:

83873
83875
83879
83882
three
one
four
two
83882

标签: bash shell pid
2条回答
看我几分像从前
2楼-- · 2019-07-10 11:10

Don't send the append operation itself to the background. Putting an & after the content you want to background but before the append suffices: The sleep and echo are still backgrounded, but the append is not.

process_ids=( )
append() { process_ids+=( "$1" ); }       # POSIX-standard function declaration syntax

{ sleep 1 && echo 'one'; } & append "$!"
{ sleep 5 && echo 'two'; } & append "$!"
{ sleep 1 && echo 'three'; } & append "$!"
{ sleep 5 && echo 'four'; } & append "$!"

echo "Background processes:"              # Demonstrate that our array was populated
printf ' - %s\n' "${process_ids[@]}"

wait
查看更多
萌系小妹纸
3楼-- · 2019-07-10 11:22

My guess is that whenever you send a function call to the background, it has a copy of the global variable on its own, so they're appending the PID to four independent copies of PROCESS_IDS. That's why every function call finds that it is empty and stores a single PID in it.

http://www.gnu.org/software/bash/manual/bashref.html#Lists

If a command is terminated by the control operator ‘&’, the shell executes the command asynchronously in a subshell. This is known as executing the command in the background.

If you want to collect the outputs from all four function calls, let them write to disk and read the output at the end:

function append {
    echo $1 | tee /tmp/file.$1
}

sleep 1 && echo 'one' & append $! &
sleep 5 && echo 'two' & append $! &
sleep 1 && echo 'three' & append $! &
sleep 5 && echo 'four' & append $!
wait

cat /tmp/file.*

Edit: this is just a proof of concept - don't do it with file system anyway (as William pointed out, this is going to be error prone unless you take care of uniqueness and synchronization). I only wanted to illustrate that you need to find another way of getting the information out of the subshells.

查看更多
登录 后发表回答