4

In shell-mode when you exit the shell process (e.g. by typing exit) the buffer still hangs around (with the message "Process shell<1> finished").

How can I automatically kill the shell buffer when the shell process exits?

(1) Is there a package available for this?

(2) How do I write the correct piece of function advice for it? Noticing that 'shell-write-history-on-exit is called on exit I tried:

(defun leo-kill-shell-buffer-on-exit ()
  (kill-buffer))

(advice-add 'shell-write-history-on-exit :after #'leo-kill-shell-buffer-on-exit)

but nothing happens. What's going wrong?

  • Your advice function should take the arguments &rest _. – Tobias Mar 13 at 7:07
  • Please have a look into shell-mode. You need to have comint-input-ring-file-name set if you want to use shell-write-history-on-exit. – Tobias Mar 13 at 7:12
  • shell-mode tries to set comint-input-ring-file-name from the environment variable HISTFILE or if that does not exist it tries to set it to some defaults like ~/.bash_history. The automagic setting did not work for me, but (setenv "HISTFILE" "~/.bash-history") before starting the shell did work. Nevertheless your add-on sentinel did not run yet. – Tobias Mar 13 at 7:28
  • 1
    Just to check, did you confirm that shell-write-history-on-exit is called? That sentinel is set only for shells matching shell-dumb-shell-regexp – phils Mar 13 at 7:29
2

Use a process sentinel when you want to react to a process exiting. e.g.:

Refer to C-hig (elisp)Sentinels

In this case shell only calls shell-mode when (and after) starting the inferior process, so we can use shell-mode-hook to add the sentinel.

As @Tobias points out in the comments, set-process-sentinel will clobber any existing sentinel for that process. shell will always have a sentinel (exactly what it is can vary), and we can call it first.

(defun my-shell-mode-hook ()
  "Custom `shell-mode' behaviours."
  ;; Kill the buffer when the shell process exits.
  (let* ((proc (get-buffer-process (current-buffer)))
         (sentinel (process-sentinel proc)))
    (set-process-sentinel
     proc
     `(lambda (process signal)
        ;; Call the original process sentinel first.
        (funcall #',sentinel process signal)
        ;; Kill the buffer on an exit signal.
        (and (memq (process-status process) '(exit signal))
             (buffer-live-p (process-buffer process))
             (kill-buffer (process-buffer process)))))))

(add-hook 'shell-mode-hook 'my-shell-mode-hook)

Alternatively, @Tobias has provided an alternative using advice-add:

(defun add-process-sentinel (sentinel &optional process)
  "Add SENTINEL to PROCESS.
PROCESS defaults to the process of the current buffer.
Use this function with care.
If there is already a process sentinel SENTINEL is used as after-advice.
That can fail if the process sentinel is reset by some other function."
  (unless process
    (setq process (get-buffer-process (current-buffer))))
  (let ((old (process-sentinel process)))
    (cond
     ((symbolp old)
      (advice-add old :after sentinel))
     ((null old)
      (set-process-sentinel process sentinel))
     (t (warn "Cannot set sentinel %S for process %S." sentinel process)))))

(defun my-shell-mode-hook ()
  "Custom `shell-mode' behaviours."
  ;; Kill the buffer when the shell process exits.
  (add-process-sentinel
   (lambda (process signal)
     (and (memq (process-status process) '(exit signal))
          (buffer-live-p (process-buffer process))
          (kill-buffer (process-buffer process))))))

(add-hook 'shell-mode-hook 'my-shell-mode-hook)
  • 1
    No good. This deactivates the process sentinel installed by shell-mode itself, e.g., shell-write-history-on-exit. If the OP set comint-input-ring-file-name the method of advising shell-write-history-on-exit is the right way of installing the additional sentinel. (AFAIK) – Tobias Mar 13 at 7:09
  • Even if there is no shell-write-history-on-exit there is an internal-default-process-sentinel. But, if you just exit maybe you can override that one. – Tobias Mar 13 at 7:45
  • I've added add-process-sentinel which is hopefully working under all present circumstances for this question. Roll back or edit if you see problems with this add-on. – Tobias Mar 13 at 7:52
  • @Tobias, I suggest you write that as a separate answer? – phils Mar 13 at 7:53
  • I consider add-process-sentinel really only as an add-on to your answer. I would only split it off if you insist. Note, that I know that the current version of add-process-sentinel is fragile. It does not work for the general case where there is already a lambda as a sentinel. One could take that lambda and call it from the new sentinel, but I did not want to go that far. – Tobias Mar 13 at 7:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.