Bash – Undo tab expansion

bashcommand linekeyboard shortcuts

In bash, pressing Tab invokes the programmable completion feature and either (a) expands to a unique path or command matching what's typed or (b) a list of possible options.

Is it possible to undo the entire expansion made by pressing Tab?

Example:

  1. My current command is ls application/views/.
  2. There are two possible expansions on that command: helpers/ and scripts/.
  3. I type heTab, which expands to helpers/.
  4. I realize I meant the other one scripts.
  5. Now I have to delete the entire expansion "helpers/".

I usually just backspace over the entire path (helpers/ in this case), because I find striking readline undo Control+_ tedious. What I'd like is a single key sequence that deletes the full match of the expansion, regardless of any characters I initially typed and any spaces or slashes added to the end.


Running list of key sequences suggested by comments or my own read of the GNU reference. None of these accomplish the goal:

  • Meta+b — Moves back to the previous / or , but doesn't delete.
  • Meta+Del — Deletes one character, not the entire word.
  • Meta+Backspace — Moves back one character without deleting.
  • Control+W — Deletes the entire path, not just the expansion. So in my example, after pressing Tab, bash expands that to ls application/views/helpers, then when I press Control+W, bash deletes application/views/helpers.

If relevant, I'm using the vi editing mode on a Gentoo Linux box. I've checked my configuration and I've not remapped any characters.

Best Answer

You're giving the keystrokes, but not the readline bindings, this can explain differences in observed behaviour (try bind -p).

The only way I can think of to do exactly what you ask is to use a custom completion function that saves the buffer state, and a separate key binding which invokes a companion shell function to restore it.

In addition to @Mark Plotnick's excellent suggestion to use menu-complete, other readline/bash options are:

  • shell-backward-kill-word this removes a "shell word" which in the case of an expanded file or path will be the complete file or path name
  • backward-kill-word this removes a "readline word", which in the case of a file or path will be a file or directory name component, including the trailing /
  • undo which undoes each change atomically, but this almost certainly requires several attempts to fully remove a complete expansion, this is almost certainly the function you have bound to to ctrl-_ (bash default)
  • set-mark and kill-region are lesser used readline features, set-mark marks the current cursor position, and kill-region deletes from the mark to the cursor. This requires you to invoke set-mark (default meta-space) in advance so that you can later use kill-region to delete back to it (no default binding)
  • possible-completions (bound to meta-=) which lists the completions without modifying the command buffer

You could also write your own function to modify the input buffer, and bind it to a function (here's one I made earlier) but I don't see how that would be very much better than backward-kill-word in this case.

You can test bindings on the fly without using .inputrc using bash's bind command, e.g.:

bind "\C-o: backward-kill-word"
bind "\C-o: shell-backward-kill-word"

backward-kill-word is the best fit, default binding is metactrl-h. set-mark/kill-region would be better, but I can't think of a neat way to automate the use of this though.

Related Question