Skip to content
shellmap

Run multiple commands in parallel

Execute several commands concurrently and wait for them all — useful for batch downloads, fan-out tasks, and CI test sharding.

How to run multiple commands in parallel in each shell

Bashunix
cmd1 & cmd2 & cmd3 & wait
Zshunix
cmd1 & cmd2 & cmd3 & wait
Fishunix
cmd1 & cmd2 & cmd3 & wait

Fish 3.0+ supports `&` for background + `wait` to block. Fish 2.x lacked `wait` — upgrade or use `disown` for fire-and-forget. The `&` syntax is identical to bash.

PowerShellwindows
$jobs = "url1","url2","url3" | ForEach-Object -Parallel { curl $_ } -ThrottleLimit 5; $jobs

`ForEach-Object -Parallel` is pwsh 7+ ONLY. 5.1 falls back to `Start-Job` (heavyweight — each job spawns a separate pwsh process; minutes for 100 jobs vs seconds for `ForEach -Parallel`). `-ThrottleLimit` caps concurrency (default 5). Each script-block runs in its own runspace — `$using:VAR` to import outer-scope variables.

cmd.exewindows
start /b cmd1 & start /b cmd2 & start /b cmd3

`start /b` runs in background. cmd has NO `wait`-equivalent for background processes — you'd need to track PIDs (`tasklist /fi "pid eq …"`) and poll. For real fan-out work shell out to PowerShell.

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • `xargs -P N` runs at most N commands in parallel, reading args from stdin: `printf "%s\n" url1 url2 url3 | xargs -n1 -P3 curl -O`. `-n1` means "1 arg per invocation" — without it xargs batches all args into a single command. `-P 0` means "unlimited parallelism" (one process per arg) — usually too much; cap at the CPU count (`-P $(nproc)`).
  • GNU `parallel` is the most ergonomic for non-trivial fan-out: `parallel -j4 curl -O ::: url1 url2 url3 url4`. `-j4` is the concurrency cap; `:::` separates the command from its arg list. Built-in support for progress (`--bar`), tagged output prefixing (`--tag`), citation suppression (`parallel --citation`), and load-aware throttling (`--load 80%`). Not in BSD/macOS base — `brew install parallel`.
  • pwsh `ForEach-Object -Parallel` PER-RUNSPACE quirk: each runspace has its OWN copy of imported modules. Heavy modules (`Az.*`, `AWS.Tools.*`) cost 1–3s per runspace to import. For tight inner loops with shared module dependencies, use `-Parallel` with a small `-ThrottleLimit` (4–8) and pre-import modules in a `BeginBlock`. Or fall back to `Start-ThreadJob` (separate module, faster startup than `Start-Job`).
  • Wait for ALL background jobs to finish: bash `wait` (no args) blocks for every backgrounded process. To collect exit codes: `wait $pid1; rc1=$?; wait $pid2; rc2=$?`. pwsh: `Wait-Job -Job $jobs; $jobs | Receive-Job`. For partial completion (return as-soon-as-any-finishes): `wait -n` (bash 4.3+, NOT on macOS BSD bash 3.2 — `brew install bash`). pwsh: `Wait-Job -Any`.

Related commands

Related tasks