How can I do Ctrl-Z and bg in one keypress to make process continue in background?

The Ctrl+Z sequence is handled by the terminal, not by the shell. (Useful background: What is the exact difference between a 'terminal', a 'shell', a 'tty' and a 'console'?)

Only a small, non-extensible set of functions can be bound to a key in a terminal. You can customize the keys bound to these functions but not add another function. These functions fall into three categories:

  • Command line edition in programs that don't provide their own (canonical mode, also called cooked mode (by opposition with raw mode), which you'll rarely use in practice): end of file (eof, Ctrl+D), end of line (eol, Ctrl+M), erase character left (erase, Ctrl+H), erase word left (werase, Ctrl+W), erase line (kill, Ctrl+U).
  • Manual flow control: stop (Ctrl+S), start (Ctrl+Q). Only useful on old serial terminals that dropped characters when the terminal couldn't keep up with the computer or vice versa.
  • Signalling: interrupt (intr, Ctrl+C), force quit (quit, Ctrl+\), suspend (Ctrl+Z).

Some systems, including Linux, extend that list, but I've never seen one that included backgrounding. Note that it would take support both in the terminal driver in the kernel, and in the terminal emulator. So you have to make do with suspend, which sends the foreground job a SIGTSTP signal. This triggers a SIGCHLD in the parent process of the foreground job's leader, which is usually the shell. When the shell received SIGCHLD, it calls waitpid to figure out why it was notified, and upon detection that a background job was suspended, it displays a notification (e.g. [1] + 12345 suspended mycommand) and a new prompt.

You could program the shell to send a SIGCONT signal to the foreground job. I don't think zsh has a hook for that, you'd need to patch the source code. And there's no way for zsh to know whether you really meant to interrupt the program (oops, this program may be doing the wrong thing, let me suspend it while I sort it out) or to background it.


What I do is to set the Ctrl+Z key sequence in zsh to background the current job (%%). That way, I can send the foreground job to the background by pressing Ctrl+Z Ctrl+Z. While I'm at it, I make Ctrl+Z do something else useful when the current input line is not empty (so I haven't just come back from a subprocess): “suspend” the current input line, allowing me to type another command, after which the interrupted line is pushed back into the input buffer.

fancy-ctrl-z () {
  if [[ $#BUFFER -eq 0 ]]; then
    bg
    zle redisplay
  else
    zle push-input
  fi
}
zle -N fancy-ctrl-z
bindkey '^Z' fancy-ctrl-z

^z ^z like zsh above is possible in bash, though it's a bit kludgy and I haven't run this for long:

  • In your .inputrc include the mapping "\C-z": "bg\n"
  • In the PS0 bash variable include `$(stty susp ^z)'
  • In the PROMPT_COMMAND bash variable include 'stty susp ^@'

so ^z for suspend is turned on when a command is run, then turned off for the bash prompt.