Zsh – Reuse All Arguments from Previous Command

argumentskeyboard shortcutszsh

In zsh, the key combination Alt+. is bound to insert-last-word, and it will insert the last argument from previous command on the current command line.

I am looking for a keybinding / shortcut that will act similar to Alt+., except that it will paste all the arguments from previous command on the command line .

I know I can type !* and zsh will interpret it as "reuse all arguments from previous command". But this is not quite what I am looking for. Also, it does not actually paste the arguments so that I can see them, zsh only interprets !* as such. I could hit Tab to have it expanded, but that is yet another command necessary.

I would prefer to have this as a key combination such as Alt+something, instead of having to type '!*' and hitting tab

How can I do this ?

UPDATE

After using the widget extensively for few years, I found few things that bother me: (I have the widget bound to Alt + /)

  1. it works OK once, but when I repeat again, it cycles through my zsh history from the beginning of my .zsh_history file.

Instead, I would like the widget to go backwards, from most recent history to the beginning.

  1. again, when it cycles from the beginning of my zsh history, the space between the command, and the arguments-to-be-completed is removed.

ie:

type some command with arguments:

echo 111 222 333

use widget to complete arguments from previous command:

printf <WIDGET>
printf 111 222 333

above works as expected. But when I press WIDGET again, it suddenly behaves like this:

printf <WIDGET>
printf111 222 333

ie the space between command and argumens was removed

  1. lastly, I would like to bind another key, for instance Alt+\ to do the reverse, so that when I press Alt + / one too many times, I can go back one step.

Best Answer

To insert all but the first word of the previous history entry, you could define a custom widget like:

insert-last-words() {
  emulate -L zsh
  set -o extendedglob # for the # wildcard operator

  local direction
  case $WIDGET in
    (*-reverse) direction=-1;;
    (*) direction=1;;
  esac

  if [[ ${WIDGET%-reverse} = ${LASTWIDGET%-reverse} ]]; then
    # subsequent invocations go further back in history like
    # insert-last-word

    (($+INSERT_LAST_WORDS_SKIP_UNDO)) ||
      NUMERIC=1 zle undo # previous invocation

    # we honour $NUMERIC for Alt+4 Alt+/ to go back 4 in history
    ((INSERT_LAST_WORDS_INDEX += ${NUMERIC-1} * direction))
  else
    # head of history upon first invocation
    INSERT_LAST_WORDS_INDEX=0
  fi
  unset INSERT_LAST_WORDS_SKIP_UNDO

  local lastwords
  local cmd=${history:$INSERT_LAST_WORDS_INDEX:1}

  # cmdline split according to zsh tokenisation rules
  lastwords=(${(z)cmd})

  if (($#lastwords <= 1)); then
    # return failure (causing beep or visual indication) when the
    # corresponding cmd had no arguments. We also need to remember
    # not to undo next time.
    INSERT_LAST_WORDS_SKIP_UNDO=1
    return 1
  fi

  # remove first word (preserving spacing between the remaining
  # words).
  cmd=${cmd##[[:space:]]#$lastwords[1][[:space:]]#}
  LBUFFER+=$cmd
}

zle -N insert-last-words
zle -N insert-last-words-reverse insert-last-words
bindkey '\e/' insert-last-words
bindkey '\e\\' insert-last-words-reverse
Related Question