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
cmd1 & cmd2 & cmd3 & waitcmd1 & cmd2 & cmd3 & waitcmd1 & cmd2 & cmd3 & waitFish 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.
$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.
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
- Retry a command on failure— Re-run a command until it succeeds or a retry cap is hit — useful for flaky network calls and CI race conditions.
- Run a command in the background— Launch a long-running command without blocking the current shell session.
- Send an HTTP POST request— POST a JSON body (or form fields, or a file) to a URL and capture the response.
- Kill a process by name— Terminate every running process whose executable matches a given name.