Bash Job Control

Managing background and foreground processes

⚙️ What is Job Control?

Job control allows you to manage multiple processes from a single shell. You can run commands in the background, pause and resume jobs, and switch between foreground and background execution for efficient multitasking.

#!/bin/bash
# Run command in background
sleep 10 &

Key Job Control Concepts

&

Background Jobs

Run commands in background

command &
📋

List Jobs

View all running jobs

jobs
⏸️

Suspend Jobs

Pause running processes

Ctrl+Z
▶️

Resume Jobs

Continue paused jobs

fg / bg

🔹 Running Background Jobs

Append & to any command to run it in the background, immediately returning control to your shell. Background jobs continue executing independently while you perform other tasks in the foreground. This is ideal for long-running operations like file processing, downloads, or computations that don't require immediate supervision. Background execution enables parallel processing and efficient task management within shell scripts.

#!/bin/bash
# background-jobs.sh

# Run single command in background
sleep 30 &
echo "Sleep running in background"

# Run multiple background jobs
for i in {1..3}; do
    (echo "Job $i starting"; sleep 5; echo "Job $i done") &
done

echo "All jobs started"

Output:

[1] 12345
Sleep running in background
[2] 12346
[3] 12347
All jobs started

🔹 Listing Jobs

The jobs command displays all background jobs started from the current shell session with their status and job numbers. Use jobs -l to see process IDs or jobs -r to filter only running jobs. Each job appears with its number in brackets, status (Running, Stopped, Done), and the command that started it. This provides complete visibility into concurrent processes for effective job management.

#!/bin/bash
# list-jobs.sh

# Start some background jobs
sleep 100 &
sleep 200 &
sleep 300 &

# List all jobs
jobs

# List with process IDs
jobs -l

# List only running jobs
jobs -r

Output:

[1] Running sleep 100 &
[2]- Running sleep 200 &
[3]+ Running sleep 300 &

🔹 Foreground and Background

Use fg to bring background jobs to the foreground and bg to resume suspended jobs in the background. Specify jobs by number (%1, %2) or using special notations like %% for the current job. This dynamic control lets you prioritize tasks, interact with processes when needed, then return them to background execution. It's essential for managing multiple concurrent operations efficiently.

#!/bin/bash
# fg-bg-demo.sh

# Start background job
sleep 60 &
echo "Job started: $!"

# Bring to foreground
# fg %1

# Or resume in background after Ctrl+Z
# bg %1

Interactive Usage:

$ sleep 60 &
[1] 12345
$ fg %1
sleep 60
[Press Ctrl+Z]
[1]+ Stopped sleep 60
$ bg %1
[1]+ sleep 60 &

🔹 Suspending Jobs

Press Ctrl+Z to suspend the currently running foreground job, pausing execution while keeping it in memory. Suspended jobs maintain their state and can be resumed later with fg (foreground) or bg (background). This is invaluable when you need to temporarily pause a long-running task to perform quick operations, then resume exactly where you left off without restarting the process.

#!/bin/bash
# suspend-demo.sh

echo "Starting long task..."
sleep 100

# User presses Ctrl+Z to suspend
# Then can run: bg to continue in background
# Or: fg to bring back to foreground

Interactive Usage:

$ ./suspend-demo.sh
Starting long task...
^Z
[1]+ Stopped ./suspend-demo.sh
$ jobs
[1]+ Stopped ./suspend-demo.sh

🔹 Waiting for Jobs

The wait command pauses script execution until specified background jobs complete, ensuring proper synchronization. Without arguments, wait pauses for all background jobs. Specify job numbers or process IDs to wait for specific tasks. This is crucial in scripts that launch multiple parallel operations, guaranteeing all processes finish before proceeding with dependent operations that require their results.

#!/bin/bash
# wait-demo.sh

echo "Starting parallel tasks..."

# Start multiple background jobs
sleep 3 &
PID1=$!

sleep 5 &
PID2=$!

sleep 2 &
PID3=$!

echo "Waiting for all tasks..."
wait

echo "All tasks completed!"

# Wait for specific job
sleep 10 &
wait $!
echo "Specific job done!"

Output:

Starting parallel tasks...
Waiting for all tasks...
[After 5 seconds]
All tasks completed!
[After 10 more seconds]
Specific job done!

🔹 Job Specifications

Reference jobs using various notations: %1 for job 1, %% for current job, %- for previous job, or %string for jobs starting with that string. These shortcuts make job control commands more efficient, especially when managing multiple background processes. The flexible referencing system allows quick access to specific jobs without remembering exact job numbers, streamlining interactive shell usage.

#!/bin/bash
# job-specs.sh

# Start named jobs
sleep 100 &
vim file.txt &
grep "pattern" *.txt &

# Reference jobs different ways
# fg %1          # Job number 1
# fg %%          # Current job
# fg %+          # Current job (same as %%)
# fg %-          # Previous job
# fg %sleep      # Job starting with "sleep"
# fg %?file      # Job containing "file"

Usage Examples:

$ jobs
[1] Running sleep 100 &
[2]- Running vim file.txt &
[3]+ Running grep pattern *.txt &
$ fg %vim
vim file.txt

🔹 Disown and Nohup

Use disown to remove jobs from the shell's job table, allowing them to continue after logout, and nohup to make commands immune to hangup signals. nohup redirects output to nohup.out by default, preserving results. These tools are essential for long-running processes that must survive terminal closure or user logout, ensuring critical operations complete regardless of session status.

#!/bin/bash
# disown-nohup.sh

# Start job and disown it
long-running-task &
disown

# Run with nohup
nohup long-process.sh &

# Combine for best results
nohup ./script.sh > output.log 2>&1 &
disown

Output:

[1] 12345
nohup: ignoring input and appending output to 'nohup.out'
[Process continues after logout]

🔹 Practical Job Control Example

Combine job control techniques to process multiple files in parallel while monitoring progress and ensuring completion. Start multiple background processing jobs, use jobs to monitor status, and wait for all jobs to finish before proceeding. This pattern is common in batch processing, data analysis, and automation scripts where parallel execution improves efficiency while maintaining control over the entire operation.

#!/bin/bash
# practical-job-control.sh

echo "Processing files in parallel..."

# Process multiple files in background
for file in *.txt; do
    (
        echo "Processing $file..."
        # Simulate processing
        sleep $((RANDOM % 5 + 1))
        echo "$file complete"
    ) &
done

# Show running jobs
echo "Active jobs:"
jobs

# Wait for all to complete
wait

echo "All files processed!"

# Check results
echo "Results:"
jobs

Output:

Processing files in parallel...
Active jobs:
[1] Running
[2] Running
Processing file1.txt...
file1.txt complete
All files processed!

🧠 Test Your Knowledge

What symbol runs a command in the background?