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).
Let’s implement an interactive function for it… While we could use
ivy-read directly in our implementation, it’s best to use completing-read and 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 reuse
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 and our job is done. Read on for the full snippet.
(require 'map) (require 'proced) (require 'seq) (defun ar/quick-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)) (if (eq (signal-process (map-elt selection 'pid) 9) 0) (message "killed: %s" prompt-title) (message "error: could not kill %s" prompt-title)))))
ar/quick-kill-process to my config. Go suggestions? Alternatives? Lemme know.