2

I define a shell keybinding in my ~/.bashrc like so:

bind -x '"\e\C-w":"/usr/bin/reset"'

This does make Ctrl-Alt-w launch the reset executable. However, it leaves the terminal in a state that resembles the one you get into by pressing Ctrl-v. So, for example, if immediately after resetting in this way I press Ctrl-p, instead of invoking the action of that shortcut (repeat previous command) I get ^P printed.

Any idea what's causing this problem and how to fix it?

EDIT: I am attaching a Python program that reproduces the messed-up terminal state I am trying to fix with the reset. I encounter this condition very often while debugging multi-threaded applications:

import pdb, thread, time

def interrupt():
    time.sleep(2)
    pdb.set_trace()

thread.start_new_thread(interrupt, ())
raw_input('? ')

To reproduce the messed-up state, just run the code above, and press Enter when you see the debugger invoked (it will print a bunch of text, ending with pdb.set_trace(), about two seconds after you run the program and the initial prompt appears).

4

Running reset clears the terminal display and also resets all input settings to their default. In particular, it sets the input mode to cooked, i.e. the terminal reads one line at a time before sending the whole line to the application (here, the application is bash). The terminal's line editor is an extremely primitive one that only understands backspace, nothing fancier. Bash provides a sophisticated line editor; it switches the terminal to raw mode, where each character is sent to the application as soon as it's typed.

If you find yourself with a messed up terminal (no line edition or no echo at the bash prompt), the easiest way to restore it is to run the command reset or stty sane. Usually you can type them blind and press Return. If that doesn't work (e.g. because the terminal is in cooked mode and the line submission character isn't the default one), you can run reset 2>/dev/pts/42 (terminal reinitialization) or stty sane </dev/pts/42 (input configuration reinitialization) (note the different redirections) where /dev/pts/42 is the terminal that the shell is running in. Finding the terminal name if you can't run commands in it may take a bit of guessing. From within the terminal, the command tty would display it. If you can find the right bash process in the ps output, you want the TTY column, with /dev in front.

Running these commands by typing them at the bash prompt does the right thing, but running them as part of a readline macro not so much. Bash resets the terminal settings each time it prints a new prompt, so what you do during the edition of one line doesn't last until subsequent commands.

Furthermore, if you run reset during the edition of a line, this messes up the parameters that bash relies on: in particular, it sets the terminal mode to cooked, whereas bash line edition requires that the line editor receives the input character by character. Comparing the output of stty during a bash command line edition and while not at a bash prompt, I think these are the settings you need:

bind -x '"\e\C-w": "reset; stty -icrnl -icanon -echo </dev/tty"'

If you're calling reset only to clear the display, call tput rs1 rs2 rs3 rf instead of reset.

As I wrote above, the right way to reset the terminal settings is to run reset at the bash prompt. Running it as part of a key binding doesn't work because bash restores the settings left over by the last application (the one that left the terminal settings in a mess) when it displays the next prompt. I don't think bash has any built-in feature to instead reset terminal settings to a sane default, but you can do that with user configuration if you want, with the following line in your .bashrc:

PROMPT_COMMAND="$PROMPT_COMMAND
stty sane"

If you really want to have a key binding that resets the terminal settings during line edition, you need something more sophisticated. Cause bash to run the reset command at a prompt (as opposed to as part of an editing command) then resume the current edit. This isn't easy to do in bash because bindings can only be readline macros or bash functions but you can't mix the two. The following code binds Ctrl+Meta+W to a readline macro that calls a bash function via a binding, then calls the accept-line readline function via its \C-m binding, and then calls another bash function via another binding. The bind -x bindings can only be assigned to key sequences of length 1 or 2, so I use little-used C-x LETTER combinations for the helper macros.

run_command_during_line_edition () {
  saved_READLINE_LINE=$READLINE_LINE saved_READLINE_POINT=$READLINE_POINT
  READLINE_POINT=0 READLINE_LINE=" $1"
  unset run_command_first
}
restore_saved_command () {
  READLINE_LINE=$saved_READLINE_LINE READLINE_POINT=$saved_READLINE_POINT
  unset saved_READLINE_LINE saved_READLINE_POINT
}
bind -x '"\C-xZ": "restore_saved_command"'
bind -x '"\C-xR": "reset; stty sane -icrnl -icanon -echo; run_command_during_line_edition reset"'
bind '"\e\C-w": "\C-xR\r\C-xZ"'

Again, you probably don't need all that complication — blindly typing reset at the prompt, or including stty sane in your PROMPT_COMMAND, should solve your problem. Oh, or you could switch to zsh where all of this would be a breeze.

  • Thanks a lot for the answer! How can I can stty and get its output "while not at the bash prompt? P.S. I should clarify that running reset from the command line (instead of bind -x) leaves the terminal in good state. Moreover, your solution works when nothing is wrong in the terminal. But the reason I need this keyboard binding is to reset a messed up terminal (due to Python's pdb debugger being terminated by a separate thread) and your suggested binding now fails to fix that. – Dun Peal Dec 4 '14 at 2:59
  • (Whereas the plain reset of before did fix it, but suffered from from the issue outlined above). – Dun Peal Dec 4 '14 at 3:06
  • @DunPeal To see the terminal state while not at the bash prompt, simply run stty from the bash command line. To see the terminal state while at the bash prompt, run tty from the bash prompt, note the value (e.g. /dev/pts/42), and run stty </dev/pts/42 from another terminal. The binding that I give does work when the terminal is messed up, it first calls reset which reaches a known state and then sets the options that bash needs. What went wrong for you? – Gilles Dec 4 '14 at 8:45
  • I checked the difference between stty in and out of bash as you suggested, and it seems to be precisely the one you found - -icrnl -icanon -echo - so I defined the exact same binding you posted. Unfortunately, when I execute this shortcut at the messed-up state, it doesn't fix the two problems evident in that state: 1. Keyboard output is not echoed at the prompt, i.e. whatever I type doesn't appear on screen, 2. Pressing enter doesn't go to a new line, instead I get horizontal prompts: "$ $ $ $ ..." – Dun Peal Dec 4 '14 at 18:42
  • Gilles, I edited my original question above to include a code sample that reproduces the "messed up" terminal state I am trying to fix with the reset. I'm not even entirely sure what the technical term for the problem is. I described it in my previous comment, and you can now reproduce it with that code. – Dun Peal Dec 4 '14 at 19:15

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.