Sequences of numbers can be created using seq
in bash.
seq 2 4
2
3
4
The &
will send scripts to the background, and jobs
can list what’s currently running. This will put three sleep
commands in the background and list the number of jobs running.
sleep 1 & sleep 1 & sleep 1 & jobs -r | wc -l | grep -Eow '[0-9]+'
3
In the words of chatgpt:
The
jobs -r
command in bash lists running jobs that are continued in the background
-E
: Uses extended regex syntax instead of basic regex syntax.-o
: Only prints the matched portion of the line, rather than the whole line.-w
: Only matches whole words. For example, if you use-w
to search for “cat” in a file, it won’t match “concatenate”.
The following will launch 4 commands in the background (&
), using a subshell (the parens), each echo
-ing <number> done
when it is done. To make a shell script wait for background jobs to finish, use the wait
command. This will take ~2s to finish as each command sleeps for 2 seconds.
for i in $(seq 1 4); do
(sleep 2 && echo "$i done") &
echo waiting
done;
wait # takes ~2s
waiting
waiting
waiting
waiting
4 done
2 done
1 done
3 done
The following will read
each line received from stdin
into the variable i
and echo its number.
seq 4 | while read i; do echo line $i; done
line 1
line 2
line 3
line 4
Another way of acomplishing this is using process substitution
. This allows the output of a command to be referred like its read from a file, by wrapping the command in <( )
, like this
while read i; do echo line $i; done < <(seq 4)
line 1
line 2
line 3
line 4
We can combine the above into a while loop that will wait
if there are max_bg_jobs
or more background jobs running, before launching another one. wait -n
will wait for a single background job to finish. (For wait -n
to work on macos ventura you need an upgraded bash
, e.g. by brew install bash
to install 5.2 as of may 2023)
The following will keep a maximum number of 2 jobs going at once. A final wait
ensures we wait for the last background job (or jobs, depending on how many you run and max_bg_jobs
) to finish before the program exits.
#!/usr/bin/env bash
max_bg_jobs=2
while read l; do
P="$(jobs -r | wc -l | grep -Eow '[0-9]+')"
if [[ $P -ge $max_bg_jobs ]]; then
wait -n # wait for single job
fi
(echo "$(date +'%H:%M:%S') starting $l" \
&& sleep 2 \
&& echo "$(date +'%H:%M:%S') done $l") &
done < <(seq 5)
wait # wait for remaining jobs
13:01:33 starting 1
13:01:33 starting 2
13:01:35 done 1
13:01:35 done 2
13:01:35 starting 3
13:01:35 starting 4
13:01:37 done 3
13:01:37 done 4
13:01:37 starting 5
13:01:39 done 5
Change the echo
-s that are sent into the background to e.g. curl
, and you have yourself a machinery that can limit the number of concurrent requests to a number of your choice.