bash – Insert Last Argument in Bash vi Mode Without Leading Space

bashreadlinevi-mode

Using bash in the default (emacs) mode I get the following behavior when I hit Esc, ..

$ echo hello
hello
$ hello  # I hit `<ESC>.` to insert this

Note there is no space before the word hello that is inserted when I hit Esc, ..

If I switch to vi mode and configure . I do get a leading space:

$ set -o vi
$ bind -m vi-command ".":yank-last-arg
$ echo hello
hello
$  hello  # I hit `<ESC>.` to insert this. Note the leading space.

Is there any way to configure bash/readline to avoid this leading space?

Best Answer

That really looks like a bug, but actually Bash is just trying to follow the POSIX specified behavior of _,

[count]_
Append a <space> after the current character position and then append the last bigword in the previous input line after the <space>. Then enter insert mode after the last character just appended. With a number count, append the countth bigword in the previous line.

As a workaround, add this to your ~/.inputrc. You can change the \M-h to some other unbound key if you want.

set editing-mode vi
set keymap vi-command
"\M-h":history-expand-line
".":"a!$\e\M-hA"

Now, open a new terminal. Upon striking . in normal mode,

  • !$ is insterted in the command-line.
  • \e (means Esc) goes back to normal mode.
  • \M-h triggers history-expand-line action, which expands $! to the value of the last argument.
  • A moves to the end-of-the line and enters insert mode.
$ echo "X Y Z"
X Y Z
$ "X Y Z" #<ESC>. inserts this
$ echo "X Y Z"
X Y Z
$ cat "X Y Z" #cat <ESC>. inserts this