Operations 12 min read

How to Parallelize Shell Loops on Linux Without External Tools

This guide explains why serial shell loops become a bottleneck for large‑scale tasks, then demonstrates three pure‑bash techniques—background execution, a simulated queue using arrays, and a FIFO‑based token system—to run multiple loop iterations concurrently while controlling the number of active processes.

ITPUB
ITPUB
ITPUB
How to Parallelize Shell Loops on Linux Without External Tools

Why Serial Loops Are Slow

When managing dozens or hundreds of remote Linux hosts, administrators often need to run the same command on each host. A traditional for or while loop runs each iteration sequentially, so the total runtime equals the number of iterations multiplied by the per‑iteration cost, which can be unacceptable for long‑running tasks.

Running Loops in the Background (Example 1)

The simplest way to gain concurrency is to launch each iteration as a background job and wait for all jobs to finish at the end.

#!/bin/bash
Njob=15
for ((i=0; i<$Njob; i++)); do
  echo "progress $i is sleeping for 3 seconds zzz…"
  sleep 3 &
done
wait
echo -e "time-consuming: $SECONDS seconds"

Running this script reduces the total execution time from 45 seconds (15 × 3) to roughly 3 seconds, as all sleep commands run in parallel. However, this approach provides no limit on the number of concurrent processes, which can exhaust system resources in production.

Controlling Concurrency with a Simulated Queue (Example 2)

This method keeps track of child‑process IDs in an array‑based queue, allowing a maximum number of parallel jobs ( Nproc) to run at any time.

#!/bin/bash
Njob=15   # total tasks
Nproc=5   # max concurrent jobs

function PushQue { Que="$Que $1"; Nrun=$(($Nrun+1)); }
function GenQue { OldQue=$Que; Que=""; Nrun=0; for PID in $OldQue; do [[ -d /proc/$PID ]] && PushQue $PID; done; }
function ChkQue { OldQue=$Que; for PID in $OldQue; do [[ ! -d /proc/$PID ]] && GenQue && break; done; }

for ((i=1; i<=$Njob; i++)); do
  echo "progress $i is sleeping for 3 seconds zzz…"
  sleep 3 &
  PID=$!
  PushQue $PID
  while [[ $Nrun -ge $Nproc ]]; do
    ChkQue
    sleep 0.1
done
done
wait
echo -e "time-consuming: $SECONDS seconds"

The script launches up to five sleep processes simultaneously; when a process finishes, the queue is updated and a new task starts. This prevents resource exhaustion and avoids the “slow‑process” bottleneck.

Using a FIFO Token Mechanism (Example 3)

A named pipe (FIFO) can act as a token bucket. The pipe is pre‑filled with a number of tokens equal to the allowed concurrency. Each iteration reads a token before starting and writes one back when finished, automatically throttling the number of active jobs.

#!/bin/bash
Njob=15
Nproc=5
mkfifo ./fifo.$$ && exec 777<>./fifo.$$ && rm -f ./fifo.$$   # open FD 777 on FIFO
for ((i=0; i<$Nproc; i++)); do echo "init $i" >&777; done   # fill tokens

for ((i=0; i<$Njob; i++)); do
  {
    read -u 777   # acquire token
    echo "progress $i is sleeping for 3 seconds zzz…"
    sleep 3
    echo "real time add $((i+$Nproc))" >&777   # release token
  } &
done
wait
echo -e "time-consuming: $SECONDS seconds"

This approach guarantees that at most five processes run concurrently, and because the token is returned only after the child finishes, the overall runtime matches the longest individual task rather than being inflated by a “water‑bucket” effect.

Key Takeaways

Background execution ( &) provides raw speed but lacks any control over process count.

Maintaining a PID queue lets you cap concurrency while still using pure Bash.

A FIFO‑based token bucket offers a clean, race‑free way to enforce a maximum number of parallel jobs.

When scaling to many hosts, always consider system limits (open files, I/O, network bandwidth) and avoid launching more processes than the environment can safely handle.

Additional Notes

Even with controlled concurrency, process scheduling may cause out‑of‑order completion; for pure data‑collection tasks this is usually acceptable. For more deterministic ordering, consider adding a short random delay or using a post‑processing step such as sort or spreadsheet lookup functions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BashParallel ExecutionProcess ControlFIFO
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.