79419912

Date: 2025-02-07 03:11:13
Score: 2
Natty:
Report link

Based on the answer from @KamilCuk (https://stackoverflow.com/a/64393146/5430476), finally, I got this version:

#!/bin/bash

count=0
maxcount=5

for ((i=0; i<10; ++i)); do
    { sleep 0.$i; echo "Job $i done"; } &
    count=$((count + 1))

    if ((count >= maxcount)); then
        wait
        count=0
    fi
done

# Wait for remaining background processes
wait

Maybe life would get easier if I installed the GNU parallel like the comments suggested, but what I want to do is to finish a simple backup job in a container. I don't want to install extra commands other than bash as much as possible.

So I wrapped this into a function, like:

#!/bin/bash

job() {
    local i=$1  # Job index
    shift
    local extra_args=("$@")
    echo "Job $i started with args: ${extra_args[*]}"
    sleep $((RANDOM % 5)) # some works
    echo "Job $i finished"
}

parallel_spawn_with_limits() {
    local max_limit_per_loop=$1; shift
    local job=$1; shift
    local count=0

    for ((i=0; i<10; ++i)); do
        { "$job" "$i" "$@" & }  # Run job in background with arguments
        count=$((count + 1))

        if ((count >= max_limit_per_loop)); then
            wait
            count=0
        fi
    done

    wait  # Ensure remaining jobs finish
}

Then call like this:

# example usage
parallel_spawn_with_limits 3 job "extra_arg1" "extra_arg2"
[1] 13199
[2] 13200
[3] 13201
Job 1 started with args: extra_arg1 extra_arg2
Job 2 started with args: extra_arg1 extra_arg2
Job 0 started with args: extra_arg1 extra_arg2
Job 0 finished
[1]   Done                    "$job" "$i" "$@"
Job 2 finished
Job 1 finished
[2]-  Done                    "$job" "$i" "$@"
[3]+  Done                    "$job" "$i" "$@"

# added blank lines for readability

[1] 13479
[2] 13480
Job 3 started with args: extra_arg1 extra_arg2
[3] 13481
Job 4 started with args: extra_arg1 extra_arg2
Job 5 started with args: extra_arg1 extra_arg2
Job 4 finished
Job 5 finished
Job 3 finished
[1]   Done                    "$job" "$i" "$@"
[2]-  Done                    "$job" "$i" "$@"
[3]+  Done                    "$job" "$i" "$@"

# added blank lines for readability

[1] 14004
[2] 14005
[3] 14006
Job 6 started with args: extra_arg1 extra_arg2
Job 7 started with args: extra_arg1 extra_arg2
Job 8 started with args: extra_arg1 extra_arg2
Job 7 finished
Job 6 finished
[1]   Done                    "$job" "$i" "$@"
[2]-  Done                    "$job" "$i" "$@"
Job 8 finished
[3]+  Done                    "$job" "$i" "$@"

# added blank lines for readability

[1] 14544
Job 9 started with args: extra_arg1 extra_arg2
Job 9 finished
[1]+  Done                    "$job" "$i" "$@"

Depending on your needs, you may need to add a trap function or abstract the 10 in the for loop into a new variable.

Reasons:
  • Blacklisted phrase (1): stackoverflow
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @KamilCuk
  • Low reputation (0.5):
Posted by: ktc