11 Nov 2013

Rebinding Bash Readline C-w

For the past couple months I have been forcing myself to learn and (most importantly) use more bash/readline shortcuts. Since I am more accustomed to vi than to emacs, I've been using vi input mode.

For example, say I'm typing a very long command and just about at the end of typing said command I realize that this command won't work just yet, I have to do something else before issuing the current command. Up until this point I would normally solve this problem by:
  1. holding Backspace for a couple seconds to delete the current, long command
  2. typing and issuing the new command
  3. typing in the long command all over again
Using bash/readline vi shortcuts this sequence can be improved to:
  1. hitting Esc to get out of input mode
  2. entering # to comment out but run the current command (although this appears to do nothing, it actually does put it into the history)
  3. typing and issuing the new command
  4. hitting Esc 
  5. pressing k (to go "up" in the history) twice
  6. hitting x to delete the initial # 
  7. then Enter to run the already-typed long command
Although the second sequence appears longer and more complicated, it does save a lot of typing and is actually faster and simpler (once you get the hang of it).

The one thing that has been rather annoying is the C-w binding.

While typing (i.e. in input mode) you can press C-u (for example) to delete everything on the current line you've typed in so far. Or you can use C-w to just delete the last "word". The reason I put "word" in quotes is because different commands use different definitions for word boundaries. If I'm typing in a path (e.g. /usr/local/bin/cmd), does the last path element count as a word, or the entire path? Or if I'm typing in a bunch of words separated by dashes (e.g. core-image-minimal) should C-w delete the entire phrase, or just back to the last dash?

As it turns out, by default C-w only considers "words" to be separated by whitespace. But more often I would prefer that punctuation be included too.

Thankfully the distinction between using whitespace-delimited words and punctuation-delimited words has already been considered. By default C-w is linked to the unix-word-rubout command (which only considers whitespace), but what I really want is for it to be linked to the backward-word-kill command (which also considers punctuation).

Following the advice here I simply edited my .bashrc to add:

stty werase undef
bind '"\C-w":backward-kill-word'

and now C-w will erase backwards to either the most recent whitespace or the most recent punctuation.

Note: I found that using

$ bind -P

was helpful to see which commands are currently linked to which control sequences.