Skip to content

Instantly share code, notes, and snippets.

@a904guy
Last active February 13, 2026 23:07
Show Gist options
  • Select an option

  • Save a904guy/e048c1edf31f47cdad11863a80260b7e to your computer and use it in GitHub Desktop.

Select an option

Save a904guy/e048c1edf31f47cdad11863a80260b7e to your computer and use it in GitHub Desktop.
A program similar to killall but uses case insensitive matching. I put it in /usr/local/bin/killgrep
#!/usr/bin/env bash
# killgrep: kill processes by regex match on the process name (/proc/<pid>/comm)
# Default matching is case-insensitive.
set -euo pipefail
usage() {
cat <<EOF
Usage: $(basename "$0") [-s SIGNAL] [-I] [-x] [-n] [-u USER] pattern
pattern ERE applied to full ps command line (like grep)
Options:
-s SIGNAL Signal to send (default: TERM). Examples: HUP, INT, TERM, KILL, 9
-I Case-sensitive match. Default is case-insensitive (like grep -i)
-x Exact match (treat pattern as a literal string; must match entire command line)
-n Dry run. Print matched PIDs/lines without sending signals
-u USER Only match processes owned by USER (like ps ... -u USER)
-h Show this help
EOF
}
signal="TERM"
insensitive=1
exact=0
dry_run=0
user_filter=""
while getopts "s:Ixnu:h" opt; do
case "$opt" in
s) signal="$OPTARG" ;;
I) insensitive=0 ;;
x) exact=1 ;;
n) dry_run=1 ;;
u) user_filter="$OPTARG" ;;
h) usage; exit 0 ;;
*) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
if [[ $# -lt 1 ]]; then
usage
exit 1
fi
pattern="$1"
self_pid="$$"
# Build a safe regex. Default uses ERE, -x turns into literal full-line match.
if [[ "$exact" -eq 1 ]]; then
# Escape ERE metachars, then anchor whole line.
esc="$(printf '%s' "$pattern" | sed -E 's/[][(){}.^$|*+?\\]/\\&/g')"
pattern="^${esc}$"
fi
# Build ps command. Use "ps aux" to mimic your pipeline.
# We'll parse: USER PID ... COMMAND (rest of line)
ps_cmd=(ps aux)
if [[ -n "$user_filter" ]]; then
# Most ps implementations accept `ps -u user -o ...`, but to stay close to aux output:
# filter by USER column via awk below, not ps flags, so output format stays "aux".
:
fi
# Collect matching PIDs, excluding:
# - the grep itself (like grep -v grep)
# - this script PID (avoid self)
# - the parent shell is NOT excluded here (ps|grep wouldn't exclude it unless it matches)
matches="$(
"${ps_cmd[@]}" \
| awk -v self="$self_pid" '
NR==1 { next } # skip header
{
user=$1; pid=$2;
# rebuild "command line" like grep sees it: everything from $11 to end (ps aux layout)
cmd="";
for (i=11; i<=NF; i++) cmd = cmd (i==11 ? "" : OFS) $i;
print user "\t" pid "\t" cmd;
}
' \
| {
if [[ -n "$user_filter" ]]; then
awk -F'\t' -v u="$user_filter" '$1==u'
else
cat
fi
} \
| {
if [[ "$insensitive" -eq 1 ]]; then
grep -Ei -- "$pattern" || true
else
grep -E -- "$pattern" || true
fi
} \
| grep -Fv -- $'\tgrep ' || true
)"
if [[ -z "${matches//$'\n'/}" ]]; then
echo "killgrep2: no processes matched pattern on ps command line: $1" >&2
exit 1
fi
# Extract PIDs (like awk '{print $2}') and de-dupe.
mapfile -t pids < <(printf '%s\n' "$matches" | awk -F'\t' '{print $2}' | awk '!seen[$0]++')
if [[ "${#pids[@]}" -eq 0 ]]; then
echo "killgrep2: matched lines but no PIDs extracted (unexpected)." >&2
exit 1
fi
if [[ "$dry_run" -eq 1 ]]; then
echo "Would send SIG${signal} to:"
printf '%s\n' "$matches" | awk -F'\t' '{printf " %s (%s)\n", $2, $3}'
exit 0
fi
# Like xargs kill: kill in one go if possible, else fall back.
kill_one() {
local pid="$1"
if [[ "$signal" =~ ^[0-9]+$ ]]; then
kill -"${signal}" -- "$pid"
else
kill -s "$signal" -- "$pid"
fi
}
kill_batch() {
if [[ "$signal" =~ ^[0-9]+$ ]]; then
kill -"${signal}" -- "${pids[@]}"
else
kill -s "$signal" -- "${pids[@]}"
fi
}
rc=0
if kill_batch 2>/dev/null; then
while IFS=$'\t' read -r _user pid cmd; do
echo "Sent SIG${signal} to $pid ($cmd)"
done <<<"$matches"
exit 0
fi
for pid in "${pids[@]}"; do
if kill_one "$pid" 2>/dev/null; then
cmd="$(printf '%s\n' "$matches" | awk -F'\t' -v p="$pid" '$2==p {print $3; exit}')"
echo "Sent SIG${signal} to $pid (${cmd:-unknown})"
else
cmd="$(printf '%s\n' "$matches" | awk -F'\t' -v p="$pid" '$2==p {print $3; exit}')"
echo "Failed to signal $pid (${cmd:-unknown})" >&2
rc=1
fi
done
exit "$rc"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment