How to Kill Processes Older Than Specific Time Threshold in Linux (Python Script Example)


6 views

When trying to kill processes older than a specific time threshold, most existing solutions fall short. The common approaches either:

  • Only match processes within fixed 24-hour windows (e.g., "7 days" actually means 7-8 days)
  • Rely on find commands that don't work consistently across Linux distributions
  • Fail to properly handle process start time comparisons

Here's a Python script that accurately identifies and kills processes older than a specified threshold:

#!/usr/bin/env python2.6
import os
import sys
import time
import psutil
from datetime import datetime, timedelta

def kill_old_processes(process_name, hours_threshold):
    threshold = datetime.now() - timedelta(hours=hours_threshold)
    killed = 0
    
    for proc in psutil.process_iter(['pid', 'name', 'create_time']):
        try:
            if process_name in proc.info['name']:
                create_time = datetime.fromtimestamp(proc.info['create_time'])
                if create_time < threshold:
                    proc.kill()
                    killed += 1
                    print(f"Killed PID {proc.pid} (started: {create_time})")
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue
    
    return killed

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: {}  ".format(sys.argv[0]))
        sys.exit(1)
    
    process_name = sys.argv[1]
    hours_threshold = float(sys.argv[2])
    
    print(f"Scanning for '{process_name}' processes older than {hours_threshold} hours...")
    killed = kill_old_processes(process_name, hours_threshold)
    print(f"Done. Killed {killed} processes.")

For those preferring a bash solution, here's an improved version that handles time comparisons accurately:

#!/bin/bash
PROCESS_NAME="page.py"
THRESHOLD_HOURS=1

# Calculate threshold in seconds since epoch
NOW=$(date +%s)
THRESHOLD=$((NOW - THRESHOLD_HOURS*3600))

# Find and kill old processes
ps -eo pid,etime,args | grep "$PROCESS_NAME" | while read -r pid etime args; do
    # Convert elapsed time to seconds
    if [[ "$etime" =~ ^([0-9]+)-([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$ ]]; then
        days=${BASH_REMATCH[1]}
        hours=${BASH_REMATCH[2]}
        minutes=${BASH_REMATCH[3]}
        seconds=${BASH_REMATCH[4]}
        total_seconds=$((days*86400 + hours*3600 + minutes*60 + seconds))
    elif [[ "$etime" =~ ^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$ ]]; then
        hours=${BASH_REMATCH[1]}
        minutes=${BASH_REMATCH[2]}
        seconds=${BASH_REMATCH[3]}
        total_seconds=$((hours*3600 + minutes*60 + seconds))
    elif [[ "$etime" =~ ^([0-9][0-9]):([0-9][0-9])$ ]]; then
        minutes=${BASH_REMATCH[1]}
        seconds=${BASH_REMATCH[2]}
        total_seconds=$((minutes*60 + seconds))
    else
        continue
    fi
    
    # Compare against threshold
    if [ "$total_seconds" -gt "$((THRESHOLD_HOURS*3600))" ]; then
        echo "Killing PID $pid (running for $total_seconds seconds)"
        kill -9 "$pid"
    fi
done

To run this daily, add to your crontab:

0 * * * * /path/to/process_killer.py page.py 1 >> /var/log/process_killer.log 2>&1

Consider these additional safeguards:

  • Add process whitelisting for critical services
  • Implement logging to track killed processes
  • Add email alerts when multiple processes are terminated
  • Consider process dependencies before killing

When managing long-running Python processes like page.py in Apache, we often need to terminate stale processes that exceed a certain age threshold. The standard approaches found online frequently fail because they either:

  • Use imprecise time range matching (e.g., "7 days" actually means 7-8 days)
  • Rely on find commands that don't work consistently across Linux distributions
  • Don't properly handle the "greater than X hours" use case

Here's a production-tested approach that works on CentOS, Gentoo, and most Linux distributions:

#!/bin/bash
# Kill page.py processes older than 1 hour
ps -eo pid,etime,args | \
awk '/page\.py/ {
    # Extract days if present
    if (index($2,"-")) {
        split($2,d,"-");
        $2=d[2]; 
        days=d[1]
    }
    # Convert elapsed time to seconds
    split($2,t,":");
    secs = t[1]*3600 + t[2]*60 + t[3];
    if (days) secs += days*86400;
    # Kill if older than 3600 seconds (1 hour)
    if (secs > 3600) {
        print $1;
    }
}' | xargs -r kill

For systems where ps output format varies, we can check process start time directly:

#!/bin/bash
threshold=$(date -d "1 hour ago" +%s)

for pid in $(pgrep -f "page\.py"); do
    start_time=$(stat -c %Y /proc/$pid)
    if [ -n "$start_time" ] && [ "$start_time" -lt "$threshold" ]; then
        kill $pid
    fi
done

For daily execution via cron, save this as /usr/local/bin/cleanup_page_procs:

#!/bin/bash
LOG=/var/log/page_proc_cleanup.log
echo "[$(date)] Starting cleanup" >> $LOG

ps -eo pid,etime,args | \
awk '/page\.py/ {
    split($2,t,":");
    secs = t[1]*3600 + t[2]*60 + t[3];
    if (secs > 3600) {
        system("kill -9 " $1);
        print "[$(date)] Killed PID " $1 >> "'"$LOG"'";
    }
}' 

Then add to crontab:

0 * * * * /usr/local/bin/cleanup_page_procs
  • Always test with echo before actually killing processes
  • Consider using kill -15 (SIGTERM) before kill -9
  • Log all actions for audit purposes
  • Set appropriate permissions on the cleanup script

For processes that might restart themselves, add a cooldown check:

#!/bin/bash
marker_file="/tmp/page_proc_cleanup.lock"

# Exit if last run was less than 5 minutes ago
find "$marker_file" -mmin +5 2>/dev/null | grep -q . || exit 0
touch "$marker_file"

# Rest of cleanup script...