Álvaro Ramírez
Emacs: quickly killing processes
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.
Update
I've moved dwim-shell-commands-kill-process
from my Emacs config to dwim-shell-commands.el. A few advantages:
- Killing processes is now async.
- Should anything go wrong, an error message is now accessible.
- You can easily install via MELPA.
If you prefer the previous version (without a dependency on dwim-shell-command), have a look at the initial commit.