Every so often, I need to kill the odd unresponsive process. While I really like proced
(check out Mickey Petersen's article), I somehow find myself using macOS's Activity Monitor to this purpose. Kinda odd, considering I prefer to do these kinds of things from Emacs.
What I'd really like is a way to quickly fuzzy search a list of active processes and choose the unresponsive culprid, using my preferred completion frontend (in my case ivy).
The function below gives us a fuzzy-searchable process utility. While we could use ivy-read
directly in our implementation, we're better of using completing-read to remain compatible with other completion frameworks. I'm a big fan of the humble completing-read
. You feed it a list of candidates and it prompts users to pick one.
To build our process list, we can lean on proced
's own source: proced-process-attributes
. We transform its output to an alist, formatting the visible keys to contain the process id, owner, command name, and the command line which invoked the process. Once a process is chosen, we can send a kill signal using signal-process dwim-shell-command and our job is done.
(require 'dwim-shell-command)
(require 'map)
(require 'proced)
(require 'seq)
(defun dwim-shell-commands-kill-process ()
"Select and kill process."
(interactive)
(let* ((pid-width 5)
(comm-width 25)
(user-width 10)
(processes (proced-process-attributes))
(candidates
(mapcar (lambda (attributes)
(let* ((process (cdr attributes))
(pid (format (format "%%%ds" pid-width) (map-elt process 'pid)))
(user (format (format "%%-%ds" user-width)
(truncate-string-to-width
(map-elt process 'user) user-width nil nil t)))
(comm (format (format "%%-%ds" comm-width)
(truncate-string-to-width
(map-elt process 'comm) comm-width nil nil t)))
(args-width (- (window-width) (+ pid-width user-width comm-width 3)))
(args (map-elt process 'args)))
(cons (if args
(format "%s %s %s %s" pid user comm (truncate-string-to-width args args-width nil nil t))
(format "%s %s %s" pid user comm))
process)))
processes))
(selection (map-elt candidates
(completing-read "kill process: "
(seq-sort
(lambda (p1 p2)
(string-lessp (nth 2 (split-string (string-trim (car p1))))
(nth 2 (split-string (string-trim (car p2))))))
candidates) nil t)))
(prompt-title (format "%s %s %s"
(map-elt selection 'pid)
(map-elt selection 'user)
(map-elt selection 'comm))))
(when (y-or-n-p (format "Kill? %s" prompt-title))
(dwim-shell-command-on-marked-files
(format "Kill %s" prompt-title)
(format "kill -9 %d" (map-elt selection 'pid))
:utils "kill"
:error-autofocus t
:silent-success t))))
I've pushed dwim-shell-commands-kill-process
to my config dwim-shell-commands.el. Got suggestions? Alternatives? Lemme know.
I've moved dwim-shell-commands-kill-process
from my Emacs config to dwim-shell-commands.el. A few advantages:
If you prefer the previous version (without a dependency on dwim-shell-command), have a look at the initial commit.