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.
When you are printing straight to the terminal, your shell doesn't know about it, so it doesn't know to print its prompt again. You would get similar behavior running e.g. (sleep 1; echo foo) &
.
I would suggest either not printing from your udev rule (that seems like the more usual thing to do: be quiet unless something wrong happened), or living with it, knowing that nothing is really broken here; the messages pushed straight to your terminal are parasitic as far as you shell is concerned.
Best Answer
Add at the end of your script:
See Bash Manual for more info.