To understand the problem of why Ctrl + C does not work, it is very helpful to understand what happens when you press it:
Most shells bind Ctrl + C to "send a SIGINT signal to the program that currently runs in the foreground". You can read about the different signals via man signal:
SIGINT 2 Term Interrupt from keyboard
Programs can ignore that signal, as they can ignore SIGTSTP as well:
SIGTSTP 18,20,24 Stop Stop typed at tty
(Which is what most shells do when you press Ctrl + Z, which is why it is not guaranteed to work.)
There are some signals which can not be ignored by the process: SIGKILL, SIGSTOP and some others. You can send these signals via the kill command. So, to kill your hanging / zombieying process, just find the process ID (PID). For example, use pgrep
or ps
and then kill
it:
% kill -9 PID
In relatively current versions of Emacs (e.g. in my copy of Emacs 24.2, but not the OS X's distribution of emacs 22.1), you can write Elisp code that will send a command to the emacsclient telling it to exit with an error exit status.
This is easier than it sounds.
There is a buffer-local variable, server-buffer-clients
, with the clients that are attached to the buffer. And the function server-send-string
can be used to communicate commands following the server-process-filter
protocol.
For example:
(server-send-string (car server-buffer-clients) "-error die")
causes (one of the) emacsclient(s) associated with the buffer to immediate issue the text
*ERROR*: die
and then exit with exit code 1.
So, it is pretty easy to define an interactive function that you could call from emacs itself to kill off the emacsclients:
(defun tell-emacsclients-for-buffer-to-die ()
"Sends error exit command to every client for the current buffer."
(interactive)
(dolist (proc server-buffer-clients)
(server-send-string proc "-error die")))
With the above in your .emacs
file (and a sufficiently current version of Emacs), you can invoke M-x tell-emacsclients-for-buffer-to-die
to make the emacsclients exit with error status. (And of course you could bind this function to an appropriate alternate key sequence.)
Footnote
Ideally, one would then couple the function above with a hook on the server-kill-buffer
function to accomplish the goal number (2.) in the original question. (That is, killing the buffer without saving it via C-x #
should fire off the same error exits from emacsclient.)
However, my attempts to add this to the kill-buffer-hook
have failed, because the server-kill-buffer
function is put on the front of the kill-buffer-hook
list after the server-visit-hook
hooks have been run, and so the default server-kill-buffer
function will run first. (One could fix up the kill-buffer-hook
afterward, but I am not yet sure where to put the code to do that into the Elisp control flow.)
Update: Okay, here's a really hacky way to accomplish the above:
(defun kill-buffer-with-special-emacsclient-handling ()
(interactive)
(add-hook 'kill-buffer-hook 'tell-emacsclients-for-buffer-to-die nil t)
(kill-buffer))
(global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)
Update 2: Slightly more robust variant:
(defun kill-buffer-with-special-emacsclient-handling ()
"Wrapper around kill-buffer that ensures tell-emacsclients-for-buffer-to-die is on the hooks"
(interactive)
(add-hook 'kill-buffer-hook 'tell-emacsclients-for-buffer-to-die nil t)
(kill-buffer))
;; (global-set-key (kbd "C-x k") 'kill-buffer)
(defun install-emacsclient-wrapped-kill-buffer ()
"Installs wrapped kill-buffer with special emacsclient handling.
Best not to install it unconditionally because the server is not
necessarily running."
(interactive)
(global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling))
(add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer)
Best Answer
Yes, you have to modify the "unbuffer" script to change the way "unbuffer" wait for the child process to terminate.
From expect documentation:
In "unbuffer" script, look for
exit [lindex [wait] 3]
and replace it by the following code:After that modification you will clearly see the difference. For example, running your
b.sh
script you will get something like this:wait returned: 8606 exp6 0 0 CHILDKILLED SIGSEGV {segmentation violation}
You may of cource change the value of
exit 1
by any value you wish. You can exit with 139 if you wish, but the value won't change to match the signal unless you add even more code to the "unbuffer" script.