Skip to content
Job Control and Background Processes
step 1/7

Reading — step 1 of 7

Learn

~2 min readProcess Control and Debugging

Bash's job control lets one shell juggle multiple processes — backgrounding, suspending, resuming. Critical for production scripts that run parallel work or manage long-lived children.

Background processes — &

#!/bin/bash
long_running &              # runs in background
echo "PID is $!"            # $! is the last backgrounded PID
wait                         # wait for ALL background jobs

Background processes get their own PID. The script continues. wait blocks until all background work finishes.

Parallel work

for host in alpha beta gamma delta; do
    ping -c1 "$host" > "/tmp/ping_$host.log" &
done
wait                         # wait for all 4 pings
echo "all done"

Far faster than serial — pings happen concurrently. Wait collects them.

Capturing PIDs of background jobs

pids=()
for host in "${hosts[@]}"; do
    process_host "$host" &
    pids+=($!)
done

for pid in "${pids[@]}"; do
    wait "$pid"
    echo "job $pid finished with exit $?"
done

Wait for SPECIFIC PIDs — capture their individual exit statuses.

kill — sending signals

kill 1234                    # SIGTERM (15) — polite shutdown
kill -9 1234                 # SIGKILL — uninterruptible
kill -INT 1234               # SIGINT (2) — Ctrl+C equivalent
kill -HUP 1234               # SIGHUP — reload config (convention)
kill -0 1234                 # signal 0 — just CHECK if process is alive

Common signals:

  • SIGTERM (15) — default; "please exit cleanly"
  • SIGKILL (9) — uninterruptible; can't be trapped; last resort
  • SIGINT (2) — Ctrl+C
  • SIGHUP (1) — terminal closed
  • SIGSTOP (19) — pause; can't be trapped
  • SIGCONT (18) — resume after stop

Job control commands (interactive shells)

./long &                     # start in background
jobs                         # list jobs
jobs -l                      # list with PIDs
fg                           # bring most-recent to foreground
fg %1                        # foreground job number 1
bg                           # resume most-recent in background

Ctrl+Z suspends the foreground job (sends SIGTSTP). bg resumes in background. fg brings to foreground.

These mostly matter in interactive shells — scripts use & and wait directly.

Timeouts

timeout 5 long_command       # kill after 5 seconds
timeout --kill-after=10 5 long_command   # SIGTERM after 5s, SIGKILL after 15

Returns 124 if the timeout fired. Useful for CI scripts that shouldn't hang.

disown — let process outlive shell

long_running &
disown                       # detach from this shell — survives logout
exit

Alternative to nohup long_running &. Both detach from the controlling terminal.

Common mistakes

  • Forgetting wait — script exits before background jobs finish.
  • kill -9 as first reflex — doesn't run trap handlers, can leave files corrupted. Try SIGTERM first.
  • No timeout on external commands — script hangs forever waiting for a network operation.
  • $! after multiple & — only captures the LAST one. Save each to a variable: cmd1 & pid1=$!; cmd2 & pid2=$!.
  • Not checking exit status of wait — by default wait succeeds even if children failed. Loop: for pid in ...; do wait $pid || handle_error; done.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…