To run several independent scripts faster than “one after another”, start each job in the background with &, keep its PID from $!, then wait on each PID and capture $? before another command overwrites it. The parent shell must not exit until every background child has been waited on, otherwise work can be cut off.
Tested the snippets below on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37 (2026-06-14). Timings are wall clock from
TIMEFORMAT+timeon this host.
Linux run shell script (execute shell script)
To execute shell script files you usually mark them executable and run them with a shell, or pass them explicitly to Bash:
chmod u+x ./mytask.sh
./mytask.sh # uses shebang line
# or
bash ./mytask.sh # ignores shebang for the interpreter choiceThat is the baseline for running scripts on Linux; the rest of this page assumes each child script is runnable that way.
Bash parallel execution with &, $!, and wait
Sending a command to the background frees the shell to continue immediately. $! is the PID of the last background process Bash started in this shell.
A reliable pattern: start all jobs in a loop, record PID → script name, then wait on each PID and capture $? immediately (before any other command overwrites it):
pidfile=$(mktemp)
trap 'rm -f "$pidfile"' EXIT
for script in workers/*.sh; do
bash "$script" &
echo "$! $(basename "$script")" >> "$pidfile"
done
while read -r pid name; do
wait "$pid"
rc=$?
echo "$name exit: $rc"
done < "$pidfile"On Bash 4+, an associative array avoids the temp file:
declare -A pid_to_name=()
for script in workers/*.sh; do
bash "$script" &
pid_to_name[$!]=$(basename "$script")
done
for pid in "${!pid_to_name[@]}"; do
wait "$pid"
rc=$?
echo "${pid_to_name[$pid]} exit: $rc"
doneEither way you get parallel shell behavior with correct exit status per child. Avoid piping echo ... | while read for the wait loop unless you understand subshell rules; reading a PID list from a file or iterating stored keys keeps everything in one shell.
bash parallelize for loop (minimal example)
Three tiny workers sleep different amounts and exit with different codes. Sequential work takes roughly the sum of the sleeps; parallel work takes roughly the maximum sleep.
Workers (adjust paths as needed):
# s1.sh — sleep 2, exit 5
# s2.sh — sleep 3, exit 10
# s3.sh — sleep 4, exit 15Sequential loop:
for s in s1.sh s2.sh s3.sh; do
bash "$s"
rc=$?
echo "$s exit: $rc"
doneSample timing and output from this environment:
s1.sh exit: 5
s2.sh exit: 10
s3.sh exit: 15
real 9.477 secParallel loop (background + wait per PID):
pidfile=$(mktemp)
for s in s1.sh s2.sh s3.sh; do
bash "$s" &
echo "$! $s" >> "$pidfile"
done
while read -r pid name; do
wait "$pid"
rc=$?
echo "$name exit: $rc"
done < "$pidfile"
rm -f "$pidfile"s1.sh exit: 5
s2.sh exit: 10
s3.sh exit: 15
real 4.049 secSo this parallel loop cut wall time from ~9.5s to ~4s while still printing each script’s real exit code. If you only need “all succeeded” and not per-job reporting, wait with no arguments waits for all background jobs but only leaves you the exit status of the last job you waited for—usually too weak for audits.
Other tools (when Bash loops are not enough)
For embarrassingly parallel command lines, xargs -P n (GNU coreutils) or parallel (GNU parallel) add a process pool and built-in retry/quoting features. They are still shell-style parallelism in spirit, but the article’s Bash wait pattern stays the smallest dependency-free core.
Summary
Shell script parallel execution in Bash means: bash your.sh &, store $!, then wait "$pid" and read $? right away into a variable so echo "$(basename …)" does not clobber the exit code—an easy mistake in the original “sequential status” line. That pattern covers background jobs and per-PID exit codes without extra packages. To run a script in Linux, use chmod +x and ./script or bash script first, then add parallelism when independent jobs justify the complexity.
If you need to avoid overlapping runs of the same script, see check if a script is already running. For process listing context, ps command in Linux is still the usual reference. To measure how long a sequential versus parallel batch actually took on the wall clock, see measure script or command execution time in Bash.

