Last active
December 13, 2024 11:33
-
-
Save sravioli/9783d22867c36d3f05d1cdee842e8963 to your computer and use it in GitHub Desktop.
Interactive interface to search for and kill processes using fzf.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# | |
# Interactive interface to search for and kill processes using fzf. | |
# Supports both regular and forced process termination and dynamically updates | |
# the process list. | |
# | |
# Usage: ./fzf-kill.sh [REFRESH_INTERVAL] | |
# REFRESH_INTERVAL: Optional; time in seconds to refresh the process list (default: 1.2 seconds). | |
# | |
# Dependencies: fzf, procs (ps if not found), sed, awk, curl | |
# Port for FZF to listen for interactions. | |
declare -ir FZF_KILL_PORT=6266 | |
# Ensures the FZF process monitor is stopped by setting a flag. | |
# Globals: | |
# None | |
# Arguments: | |
# None | |
# Outputs: | |
# Writes 'false' to /tmp/fzf-kill-running. | |
function __fzf::kill::ensure-stopped() { echo false >/tmp/fzf-kill-running; } | |
# Extracts the PID from a process information string. | |
# Globals: | |
# None | |
# Arguments: | |
# $1 - A line of process information. | |
# Outputs: | |
# Prints the extracted PID. | |
function __fzf::kill::get-pid() { echo "$1" | sed --expression='s/^[ \t]*//' | awk '{ print $1 }'; } | |
# Retrieves the name of a process based on its PID. | |
# Globals: | |
# None | |
# Arguments: | |
# $1 - The PID of the process. | |
# Outputs: | |
# Prints the process name. | |
function __fzf::kill::get-pname() { ps -oargs= --pid "$1"; } | |
# Checks if a process with a given PID is running. | |
# Globals: | |
# None | |
# Arguments: | |
# $1 - The PID of the process. | |
# Outputs: | |
# Prints the PID if the process is running; otherwise, errors. | |
function __fzf::kill::check-process() { ps -opid= --pid "$1"; } | |
# Interactively asks the user whether to kill a process, with optional force. | |
# Globals: | |
# None | |
# Arguments: | |
# $1 - The process information string. | |
# $2 - Optional; "true" to force kill (default: "false"). | |
# Outputs: | |
# Executes `kill` or `kill -9` based on user input. | |
function __fzf::kill::ask() { | |
local pid answer force pname | |
pid="$(__fzf::kill::get-pid "$1")" | |
pname="$(__fzf::kill::get-pname "${pid}")" | |
force="${2:-false}" | |
answer="$( | |
echo -e 'no\nyes' | | |
fzf --bind=$'start:transform-border-label: | |
if [[ '"${force}"' = false ]]; then | |
echo "$ kill '"${pid}"'? ('"${pname}"')" | |
else | |
echo "$ kill -9 '"${pid}"'? ('"${pname}"')" | |
fi' \ | |
--prompt='Choose❭ ' --preview-window='hidden' \ | |
--preview="echo \"{ pid: ${pid}, force: ${force}, pname: ${pname} }\"" \ | |
--preview-label=' Debug info' --color='label:red,preview-label:yellow' | |
)" | |
case "${answer}" in | |
yes) | |
if __fzf::kill::check-process "${pid}"; then | |
if [[ "${force}" = true ]]; then | |
kill -9 "${pid}" | |
else | |
kill "${pid}" | |
fi | |
fi | |
;; | |
no | *) return 1 ;; | |
esac | |
} | |
export -f "__fzf::kill::ask" | |
export -f "__fzf::kill::get-pid" | |
export -f "__fzf::kill::get-pname" | |
export -f "__fzf::kill::check-process" | |
export -f "__fzf::kill::ensure-stopped" | |
# Main function to interactively search and kill processes using FZF. | |
# Globals: | |
# FZF_KILL_PORT | |
# Arguments: | |
# $1 - Optional; refresh interval for process list (default: 1.2 seconds). | |
# Outputs: | |
# Executes `kill` or `kill -9` based on user actions. | |
function __fzf::kill() { | |
local fzf_flags | |
if command -v procs >/dev/null; then | |
cmd="/usr/bin/procs" | |
flags=(--color=always --theme=auto --pager=disable --use-config=default "${USER}" --sortd=CPU) | |
fzf_flags=(--header-lines=2) | |
else | |
cmd="/usr/bin/ps" | |
flags=(-u"${USER}" -o"pid,user,tty,%cpu,%mem,args" --sort=pcpu) | |
fzf_flags=(--header-lines=1) | |
fi | |
"${cmd}" "${flags[@]}" | fzf "${fzf_flags[@]}" \ | |
--listen="${FZF_KILL_PORT}" --height=100% --prompt='Search❭ ' --border-label=' Kill process' \ | |
--bind='ctrl-g:last,alt-g:first' --header='<M-k> kill / <M-f>: kill -9' \ | |
--preview-window=$'hidden,bottom,1' --preview-label=' Debug info' \ | |
--preview=$'echo "__fzf::kill::get-pid -> $(__fzf::kill::get-pid {})"' \ | |
--color='label:red,preview-label:yellow' \ | |
--bind=$'alt-k:execute(__fzf::kill::ask {})' \ | |
--bind=$'enter:execute(__fzf::kill::ask {})' \ | |
--bind=$'alt-f:execute(__fzf::kill::ask {} true)' \ | |
--bind=$'focus:transform-header( | |
echo "<M-k> $ kill "$(__fzf::kill::get-pid {})"" | |
echo "<M-f> $ kill -9 "$(__fzf::kill::get-pid {})"")' \ | |
--bind=$'start:execute-silent( | |
echo true >/tmp/fzf-kill-running | |
while "$(cat /tmp/fzf-kill-running)"; do | |
curl --silent --request POST "localhost:'"${FZF_KILL_PORT}"'" --data "reload('"${cmd} ${flags[*]}"')" && | |
sleep '"${1:-1.2}"' | |
done &)' | |
__fzf::kill::ensure-stopped | |
} | |
__fzf::kill "$1" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment