Przejdลบ do treล›ci

๐ŸŒ€ Extended Subprocesses and Subshells

Understanding process creation, subshells, and job control is essential for writing efficient shell scripts.

๐Ÿงญ Process Creation Basics

When you run a command, the shell:

  1. Forks โ€” Creates a copy of itself (child process)
  2. Execs โ€” Replaces child's memory with the command
  3. Waits โ€” Parent waits for child to complete (unless backgrounded)
1
2
3
4
5
Parent (bash)
  โ”œโ”€โ”€ fork()
  โ”‚     โ””โ”€โ”€ Child (bash)
  โ”‚           โ””โ”€โ”€ exec(ls) โ†’ ls process
  โ””โ”€โ”€ wait()

๐Ÿงช Subshells

A subshell is a child process that inherits the parent's environment but runs independently.

Creating Subshells

1
2
3
4
5
6
# Method 1: Parentheses
(
    cd /tmp
    pwd    # Shows /tmp
)
pwd        # Shows original directory
1
2
# Method 2: Pipeline (each stage runs in subshell)
echo "hello" | tr 'a-z' 'A-Z'   # Both echo and tr run in subshells
1
2
# Method 3: Command substitution
result=$(date)   # date runs in subshell

๐Ÿง  Subshell vs Background Job

Aspect Subshell (...) Background &
Runs concurrently โœ… โœ…
Inherits vars โœ… โœ…
Modifies parent โŒ โŒ
Waits for finish โœ… (implicit) โŒ (must use wait)
Can be piped โœ… โŒ

๐Ÿงช Grouping Commands

Use subshells to group operations:

1
2
3
4
5
6
(
    cd /project
    git pull
    make build
    make test
) > build.log 2>&1

All output redirected together. Changes inside don't affect parent.


๐Ÿง  Background Jobs

Launch tasks in background:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
long_task &
PID=$!
echo "Started task with PID $PID"

# Do other work
sleep 5

# Wait for completion
wait $PID
echo "Task completed"

List background jobs:

1
2
3
jobs -l
# Output:
# [1]+ 12345 Running    long_task &

Bring to foreground:

1
fg %1    # Bring job 1 to foreground

Send signal to background job:

1
kill %1   # Send TERM to job 1

๐Ÿงช Process Substitution (Advanced)

Treat command output as a file:

1
diff <(sort file1.txt) <(sort file2.txt)

Works like:

1
diff /dev/fd/63 /dev/fd/62   # Where fd/63 and fd/62 are pipes

Also for input:

1
2
3
while IFS= read -r line; do
    echo "Processing: $line"
done < <(find . -name "*.log")

โš ๏ธ Bash/Zsh feature โ€” not POSIX.


๐Ÿง  Managing Parallel Jobs

Limit concurrent background jobs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
max_jobs=4
current_jobs=0

for file in *.dat; do
    process_file "$file" &
    current_jobs=$((current_jobs + 1))

    if [ $current_jobs -ge $max_jobs ]; then
        wait   # Wait for all background jobs
        current_jobs=0
    fi
done

wait   # Final wait for remaining jobs

๐Ÿงช Daemonizing a Process

Convert script to background daemon:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
daemonize() {
    # Fork first time
    (
        # Fork second time (double-fork pattern)
        (
            # Close stdin, stdout, stderr
            exec 0</dev/null
            exec 1>/dev/null
            exec 2>/dev/null

            # Change to root directory
            cd /

            # Run actual daemon logic
            while true; do
                do_work
                sleep 60
            done
        ) &
        disown   # Detach from parent shell
    )
}

๐Ÿงพ Portability Notes

Feature POSIX Bash Zsh
Subshells (...) โœ… โœ… โœ…
Background & โœ… โœ… โœ…
$! (last PID) โœ… โœ… โœ…
jobs, fg, bg โœ… โœ… โœ…
Process substitution โŒ โœ… โœ…
disown โŒ โœ… โœ…

๐Ÿงพ Summary

  • Subshells isolate side effects โ€” use (...) for grouping.
  • Background jobs (&) enable concurrency โ€” manage with wait.
  • Process substitution is powerful but not portable.
  • Use double-fork pattern for proper daemonization.
  • Limit parallel jobs to avoid resource exhaustion.

๐Ÿ‘‰ Continue to: Pipelines and Process Substitution