Suppose I do something like:

ln a_file_with_a_long_filename.pdf ~/path/to/a/new/hardlink/a_file_with_a_long_filename_slightly_modified.pdf

Is there a way to refer to and expand a_file_with_a_long_filename.pdf if my cursor is at the end of the string ln a_file_with_a_long_filename.pdf ~/path/to/a/new/hardlink/ in zsh?

If not, what would you suggest do reduce typing work?

With the default Emacs bindings, it's just two keychords: ESC-2 ESC-^_ (i.e. Esc 2 Esc Ctrl+_ or Alt+2 Ctrl+Alt+_). That's the command copy-prev-word with the numeric argument 2 (the default argument 1 would copy ~/path/to/a/new/hardlink/). If the file name contains (quoted) spaces, you would need ESC-2 ESC-x copy-prev-shell-word RET. You may want to bind this command to more convenient key, especially if your keyboard layout requires Shift for _. You can use these commands in vi mode as well, but neither is bound to a key by default.

If you get the numeric argument wrong, press Ctrl+_ to undo then try again.

This sounds like a fun code golf challenge. Here's one option:

  1. Run an innocuous command with the filename; enter enough of the filename to allow TAB-completion.

    : a_file<TAB>
    
  2. Use !!$ to refer to the last argument of the previous command:

    ln !!$ ~/path/to/a/new/hardlink/!!$
    

Thanks to zsh's helpful quoting, this is safe even in the face of IFS-containing filenames. You'll notice that as soon as you hit space after the first !!$, zsh expands the filename; ditto if you add a gratuitous space at the end of the command.

Number of characters required is:

  • 3 x 2 = 6 for the two !!$
  • 2 for the :<SPACE>

8 + plus enough for the initial tab completion.

  • You can “golf” !!$ to ESC-., but even so this is more complicated than it should be. This isn't the 1970s anymore, shells have a line editor. – Gilles Nov 19 at 20:34

In vi mode this would be <esc>0wyt $p which goes to command mode, 0 beginning of the line, w to the next word (advance to the filename) yt yank to space (to get the long filename, assuming no spaces in the filename) and then $p to put what was just yanked at the end of the line. This is a lot quicker than describing it once you memorize the vi motions.

You could also setup a bindkey to yank-put the second argument though that's pretty specific code for such a use case, here bound to control+t but that could be whatever you want.

function yank-put {
  local -a words
  words=(${(z)LBUFFER})
  if (( $#words > 1 )); then
    BUFFER+=$words[2]
    CURSOR+=${#words[2]}
  fi
}
zle -N yank-put
autoload -U yank-put compinit
compinit
set -o vi
bindkey -M viins "^t" yank-put

There is copy-earlier-word, you could bind it to your favorite keybinding before using it with appending these lines to ~/.zshrc:

autoload -Uz copy-earlier-word
zle -N copy-earlier-word
bindkey "^[," copy-earlier-word

So, you could use like this in the command line:

% ln a_file_with_a_long_filename.pdf ~/path/to/a/new/hardlink/<Esc-,><Esc-,>

(The first keypress of Esc-,(or Alt+,) yields "~/path/to/new/hardlink/" like copy-prev-shell-word, and the second time it replaces that newly inserted word with "a_file_with_a_long_filename.pdf").


Here is a copy of copy-earlier-word document for a reference.

This widget works like a combination of insert-last-word and copy-prev-shell-word. Repeated invocations of the widget retrieve earlier words on the relevant history line. With a numeric argument N, insert the N th word from the history line; N may be negative to count from the end of the line.

If insert-last-word has been used to retrieve the last word on a previous history line, repeated invocations will replace that word with earlier words from the same line.

Otherwise, the widget applies to words on the line currently being edited. The widget style can be set to the name of another widget that should be called to retrieve words. This widget must accept the same three arguments as insert-last-word.

-- copy-earlier-word ZLE Function, widgets, zshcontrib(1)

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.