How to Get the PID of a Child Process (imapsync) in Bash Scripts for Proper Signal Handling


8 views

When working with bash scripts that launch long-running processes like imapsync, a common issue arises when the parent script gets terminated unexpectedly. The child process often continues running as an orphaned process, consuming system resources.

The simplest approach is to capture the PID immediately after launching the process:


#!/bin/bash
imapsync [options] &
IMAPSYNC_PID=$!
echo "imapsync PID is $IMAPSYNC_PID"

The magic variable $! contains the PID of the most recently executed background command.

Here's how to implement a signal handler that kills both the script and the child process:


#!/bin/bash

# Launch imapsync
imapsync [options] &
IMAPSYNC_PID=$!

# Signal handler
cleanup() {
    echo "Killing imapsync process $IMAPSYNC_PID"
    kill -9 $IMAPSYNC_PID 2>/dev/null
    exit
}

# Trap signals
trap cleanup SIGINT SIGTERM

# Wait for completion
wait $IMAPSYNC_PID

For more complex scenarios, consider using process groups:


#!/bin/bash

# Launch in new process group
set -m
imapsync [options] &
IMAPSYNC_PID=$!
IMAPSYNC_PGID=$(ps -o pgid= $IMAPSYNC_PID)

# Kill entire process group
cleanup() {
    kill -- -$IMAPSYNC_PGID
    exit
}

trap cleanup SIGINT SIGTERM
wait $IMAPSYNC_PID

If you need to find already running imapsync processes:


#!/bin/bash
# Find all imapsync processes not attached to terminal
PIDS=$(pgrep -f 'imapsync' -x)
for pid in $PIDS; do
    if [ -z "$(ps -o tty= $pid)" ]; then
        echo "Found orphaned imapsync: $pid"
        # kill $pid
    fi
done
  • Always store the PID immediately after process launch
  • Use proper signal handling (SIGINT, SIGTERM)
  • Consider using process groups for complex scenarios
  • Clean up orphaned processes during script initialization

When dealing with process management in bash scripts, a common challenge arises when child processes don't properly terminate when their parent script is killed. This is particularly problematic with long-running processes like imapsync where orphaned processes can consume system resources.

While $! gives the PID of the most recent background process, this approach fails in several scenarios:

imapsync [options] &
child_pid=$!
# This only works for direct background execution

Method 1: Process Substitution with exec

exec imapsync [options] > output.log 2>&1 &
imapsync_pid=$!

Method 2: Using pgrep in Combination

imapsync [options] &
sleep 1 # Allow process to start
imapsync_pid=$(pgrep -P $$ imapsync)

Method 3: PID File Creation

imapsync [options] & echo $! > /var/run/imapsync.pid
# Later in signal handler:
kill $(cat /var/run/imapsync.pid)

Here's a complete solution with signal trapping:

#!/bin/bash

cleanup() {
    if [ -n "$imapsync_pid" ]; then
        kill -TERM "$imapsync_pid" || :
        wait "$imapsync_pid" 2>/dev/null
    fi
    exit 0
}

trap cleanup SIGINT SIGTERM

imapsync [options] &
imapsync_pid=$!

wait "$imapsync_pid"

For more complex scenarios, consider process groups:

set -m # Enable job control
imapsync [options] &
imapsync_pgid=$(ps -o pgid= -p $!)
trap "kill -- -$imapsync_pgid" EXIT

If you suspect processes are still leaking:

pstree -p $$
# Or for imapsync specifically:
ps aux | grep '[i]mapsync'