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'