This page contains a patch to
bash, which introduces a third mode of job control notification. Written by Simon Tatham.
As shipped by default,
bash has two modes of job control notification:
set +b(the default mode)
bashis about to print its prompt, it first checks to see whether any background jobs have stopped or terminated, and if so it prints notification messages before displaying the prompt.
SIGCHLDsignal, and its signal handler displays the notification message immediately - even if
bashis currently in the middle of waiting for a foreground process to complete.
Neither of these modes is really satisfactory.
set -b has no awareness of the state of the terminal, so its notifications are likely to corrupt the display of any full-screen application you might be running (such as a text editor or MUA). In particular, this mode cannot even interact sensibly with
bash's own command line editing! Try running ‘
sleep 1 &’ in this mode, and then immediately beginning to type a command line. You will find that when the notification is printed, you are left with the display in a somewhat non-obvious state.
set +b never corrupts the display, but it has its own problems. In particular, suppose you are testing a GUI program whose source code you're editing in a separate editor window. (Let's assume it's written in an interpreted language.) You might have a terminal window in which you keep typing the same command, ‘
myprogram &’. Every time you make changes to the program and want to restart it, you close down the current instance of it, and repeat this command in your shell window. Now
bash will launch the new instance of the program before printing the notification that the old one has finished - and because printing that notification is the moment at which the job number allocation gets reset, this also means that your job numbers will increase without bound unless you deliberately hit Return at a point when no background job is running. Eventually one instance of your program will hang and you'll have to type ‘
kill %134’, instead of the ‘
kill %1’ you would prefer.
What's needed is a third mode.
If you press ^X ^V at a normally configured
bash prompt, the shell will move to a new line on the terminal, display its version information, and then redisplay the command line you were editing beforehand, so you don't lose track of what you were doing. This is how job terminations should be displayed.
The patch I provide below implements this third mode. Like the default
set +b mode,
bash will never interrupt a running foreground application to tell you about a job termination - so you don't need to worry that full-screen applications will have their displays corrupted. But while actually sitting at the shell command prompt, job notifications are displayed immediately rather than waiting for you to press Return; and when a notification is shown, your current command line is redisplayed immediately afterwards so that you don't lose track of whatever command-line editing you were doing.
This patch adds a new setting to
set command, called
notify-sensibly. You enable this feature using
set -o notify-sensibly, and disable it again using
set +o notify-sensibly.
notify-sensibly feature has the effect of changing the
set -b mode from the normal one into my new one. So to enable my new semi-asynchronous mode, you have to do both
set -o notify-sensibly and
set -b. (Yes, this is ugly; but the
set framework doesn't make it syntactically easy to implement a three-way switch.)
set +b is usually preferable to
set -b in the absence of my feature. Therefore, if you use the same
.bashrc on more than one system, I recommend the following code fragment to ensure you get optimal behaviour on versions of
bash both with and without my patch:
set -o notify-sensibly 2>/dev/null && set -b
(On an unpatched
bash, the attempt to enable
notify-sensibly will print an error message - here redirected to
/dev/null - and return failure, so that the subsequent
set -b will not be executed. On a patched
bash, both commands will complete silently and successfully and my third mode will be enabled.)
Here are patches to several versions of
bash. (The various Linux systems I use do not all run the same distribution or version of
bash, so I prepared patches against several different versions.)
patch-bash-3.1-notify(also seems to apply fine against 3.2)
In addition, here are Debian binary packages (for
i386) of the patched
bash. These should install cleanly on any system running Debian 5.0 (‘lenny’), 4.0 (‘etch’), 3.1 (‘sarge’) or 3.0 (‘woody’).
In case it's useful to anybody, here's a brief overview of what I did.
set -b mode works by actually displaying the notifications in the
SIGCHLD handler. In my first attempt to code this patch (around 1997), I initially tried to imitate this structure. However, it proved difficult to safely determine whether an instance of
readline was in progress or not - setting a flag just before calling
readline left a race condition whereby, if a job terminated at just the wrong moment,
rl_redisplay() would be called before
readline was initialised. It worked well enough in practice, and I ran it on my own machine in full knowledge of the risks, but it wasn't good enough to distribute more widely.
When I came back to the problem in 2003, I set up an internal pipe within the
bash process. The
SIGCHLD handler simply writes a byte to this pipe; and instead of calling
readline() to read an entire command line, I now use the
rl_callback_handler_install() interface to the Readline library. This allows me to do my own
select() between the input file descriptor and the internal pipe used to indicate
SIGCHLD. So if a job terminates in the middle of command-line editing, the signal handler writes to the pipe, which immediately terminates the
select(), and my code can print the job notification, call
rl_redisplay() in the knowledge that it's safe to do so, and go back to editing. Whereas if a job terminates while
bash is doing anything else, the byte written to the signal pipe will simply sit there until
bash is about to print its prompt, at which point it will do the usual
set +b check for job terminations and also empty the pipe buffer.
I wrote this patch in July 2003. It spent about a year in alpha testing, and then in July 2004 I published it on the web for other people to beta-test. I mailed the
bash maintainers about it at that time as well, but they never replied. (I certainly wouldn't blame them if they thought it was far too hacky!)
Therefore I'm resigned to maintaining this patch as a third-party addition to
bash, and I expect to adapt it to new releases of
bash as and when I find myself dealing with those releases.