#!/usr/bin/env bash set -euo pipefail # Script Name: murder # Description: Gracefully terminate processes with escalating signals # Source: https://evanhahn.com/scripts-i-wrote-that-i-use-all-the-time/ # Credit: Evan Hahn - https://codeberg.org/EvanHahn/dotfiles # Usage: murder 1234 # kill PID # murder :8080 # kill process on port 8080 # murder firefox # kill process by name # murder # interactive fzf picker (integrated with your k alias!) # Signal escalation: [signal, wait_seconds] # SIGTERM (15) -> SIGINT (2) -> SIGHUP (1) -> SIGKILL (9) SIGNALS=("15 3" "2 3" "1 4" "9 0") murder_pid() { local pid=$1 if ! ps -p "$pid" > /dev/null 2>&1; then echo "Process $pid not found" >&2 return 1 fi local process_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown") echo "Terminating $process_name (PID: $pid)..." for sig_wait in "${SIGNALS[@]}"; do read -r sig wait <<< "$sig_wait" if ! ps -p "$pid" > /dev/null 2>&1; then echo "✓ Process terminated successfully" return 0 fi case $sig in 15) echo " → Sending SIGTERM (polite shutdown)..." ;; 2) echo " → Sending SIGINT (interrupt)..." ;; 1) echo " → Sending SIGHUP (hangup)..." ;; 9) echo " → Sending SIGKILL (force kill)..." ;; esac kill -"$sig" "$pid" 2>/dev/null || true if [[ $wait -gt 0 ]]; then sleep "$wait" fi done if ps -p "$pid" > /dev/null 2>&1; then echo "✗ Failed to terminate process" >&2 return 1 fi echo "✓ Process terminated" } # If no arguments, use fzf to select process (like your k alias!) if [[ $# -eq 0 ]]; then if ! command -v fzf &>/dev/null; then echo "Error: fzf not found. Install fzf or provide PID/name/port as argument." >&2 exit 1 fi selected=$(ps aux | fzf --header="Select process to terminate (graceful escalation)" \ --header-lines=1 \ --preview='echo "Will attempt: SIGTERM → SIGINT → SIGHUP → SIGKILL"') if [[ -n "$selected" ]]; then pid=$(echo "$selected" | awk '{print $2}') murder_pid "$pid" fi exit 0 fi # Parse argument: PID, :port, or process name arg="$1" if [[ "$arg" =~ ^[0-9]+$ ]]; then # Argument is a PID murder_pid "$arg" elif [[ "$arg" =~ ^:[0-9]+$ ]]; then # Argument is a port (e.g., :8080) port="${arg:1}" pid=$(lsof -ti ":$port" 2>/dev/null || true) if [[ -z "$pid" ]]; then echo "No process found listening on port $port" >&2 exit 1 fi echo "Found process on port $port:" ps -p "$pid" -o pid,comm,args | tail -1 murder_pid "$pid" else # Argument is a process name pids=$(pgrep -f "$arg" || true) if [[ -z "$pids" ]]; then echo "No processes found matching: $arg" >&2 exit 1 fi # If multiple processes, show list and let user choose count=$(echo "$pids" | wc -l) if [[ $count -gt 1 ]]; then echo "Multiple processes found matching '$arg':" ps -p $pids -o pid,comm,args if command -v fzf &>/dev/null; then selected=$(ps -p $pids | fzf --header-lines=1) if [[ -n "$selected" ]]; then pid=$(echo "$selected" | awk '{print $1}') murder_pid "$pid" fi else echo -n "Enter PID to terminate: " read pid murder_pid "$pid" fi else murder_pid "$pids" fi fi