34

I was just typing something along the lines of:

mv foo/bar/poit/zoid/narf.txt

Suddenly I realized, damn, I have to type large parts of that parameter again:

mv foo/bar/poit/zoid/narf.txt foo/bar/poit/zoid/troz.txt

Even with tab completion, that's quite a pain. I know I can copy-paste the parameter by mouse-selecting the text and middleclicking but that is not good enough. I want to keep my hands on the keyboard.

Is there a way to copy-paste the current parameter of the line using the keyboard?

k0pernikus
  • 15,463
  • 3
    My advice doesn't answer your question, but is a small tip. As in your example, you can use next construction: mv foo/bar/poit/zoid/{narf.txt,troz.txt} instead ot typing/copypasting long address twice. – rush Jun 06 '12 at 10:05
  • 3
    Slightly shorter than @Rush's solution: mv foo/bar/poit/zoid/{narf,troz}.txt. This works especially well if we have something like .../zoid/this_source_file_has_to_be_patched.c{,.orig} – Ansgar Esztermann Jun 06 '12 at 10:14
  • @Rush Do you mind pointing it out as an answer? It may not be directly what I asked, but then again, your solution is much more to the point of what I want. It deserves a token of acceptance. – k0pernikus Jun 06 '12 at 10:21

9 Answers9

31

History expansion actually works on the current command as well, using the event designator !#. Combine this with the word designator for the last argument, $, to get the parameter you just typed.

And you can use all the regular modifiers over it, so if, e.g., you are renaming a file in a far-away directory, you can just type:

mv path/you/do/not/want/to/type/twice/oldname !#$:h/newname
qwertyboy
  • 411
  • Very useful! This works especially well with bash's magic-space option in inputrc to automatically expand history expansions. (I couldn't use the top-voted answer because I've set ^W to break at slashes since that's usually more useful.) – Soren Bjornstad Jan 22 '16 at 17:40
  • 2
    @SorenBjornstad alt+backspace usually already stops at word delimiters like slashes, so you needn't change how ^W to get that functionality, you can have both delete non-word-character or whitespace delimited words – Eric Renouf Dec 17 '16 at 13:13
21

If I've planned ahead, I use brace expansion. In this case:

mv foo/bar/poit/zoid/{narf,troz}.txt

Here is another approach using the default readline keyboard shortcuts:

  • mv foo/bar/poit/soid/narf.txt: start
  • Ctrl-w: unix-word-rubout to delete foo/bar/poit/soid/narf.txt
  • Ctrl-y, Space, Ctrl-y: yank, space, yank again to get mv foo/bar/poit/soid/narf.txt foo/bar/poit/soid/narf.txt
  • Meta-backspace, Meta-backspace: backward-kill-word twice to delete the last narf.txt
  • troz.txt: type the tail part that is different

If you spend any non-trivial amount of time using the bash shell, I'd recommend periodically reading through a list of the default shortcuts and picking out a few that seem useful to learn and incorporate into your routine. Chapter 8 of the bash manual is a good place to start. Knowing the shortcuts can really raise your efficiency.

wjandrea
  • 658
jw013
  • 51,212
  • Neat! In my case I had to press Alt + Backspace instead of Meta + Backspace. – k0pernikus Jun 07 '12 at 09:44
  • 3
    @k0pernikus Yes on most keyboards, the Alt key is the Meta key. – jw013 Jul 12 '12 at 16:19
  • 2
    To save retyping .txt, you could press Ctrl+Left (or any other shortcut that calls backward-word), then Left one character, then Alt+Backspace and type troz. – wjandrea May 30 '19 at 18:32
14

As in your example, you can use next construction:

mv foo/bar/poit/zoid/{narf.txt,troz.txt}

or even (as suggested Ansgar Esztermann):

mv foo/bar/poit/zoid/{narf,troz}.txt

instead ot typing/copypasting long address twice.

rush
  • 27,403
8

This inserts the last word of the Readline line buffer. !# is the current line, :$ is the last word, and \e^ is history-expand-line.

"\ej":"!#:$\e^"

This also works when you're in the middle of a word, and it doesn't require inserting a space manually, but it has the disadvantage that it overrides the kill ring:

"\ee":shell-backward-word
"\ei":shell-forward-word
"\ew":copy-region-as-kill
"\ej":"\ee\e \ei\ew \C-y"

In order to duplicate the previous whitespace-delimited word instead of a shell word, you can use \C-w\C-y\C-y. \C-w deletes a whitespace-delimited word backwards.

nisetama
  • 1,097
7

Playing around I got this to work:

mv foo/bar/poit/zoid/narf.txt

Hit Enter
to store the last parameter.

Now use to get last typed in line back. Enter a space and to get the last used parameter use:

Alt + .

I hate provoking an error, but it gets the job done in this use case.

k0pernikus
  • 15,463
3

You can easy use variables:

a=test.csv ; cp $a $a.bak

Or in you case (note tab competition works for the a part):

a=foo/bar/poit/zoid/ ; mv ${a}narf.txt ${a}troz.txt
ppp
  • 41
2

A short interactive two step solution

  1. type: echo foo/bar/poit/zoid/narf.txt

    This makes foo/bar/poit/zoid/narf.txt available for the Alt-. shortcut in bash.

  2. type: mv and hit Alt-., Space, Alt-.

    You will get mv foo/bar/poit/zoid/troz.txt foo/bar/poit/zoid/troz.txt. Now you can modify the last word easily.

Midnight Commander

This tool which might be (very) handy is a file manager running in the console. It gives you the possibility to copy filenames and directory paths interactively into the command line. Midnight Commander shows you therefore 2 panels (left and right), which list the contents of different directories.

A short guide:

  • Up/Down arrow keys select files in the current panel.
  • Tab switches between left and right panel.
  • Ctrl-Shift-Enter copies the currently selected file to the command line (including full path)
  • Alt-o opens the currently selected directory in the other panel
  • Ctrl-o lets you see the console in full screen
  • Ctrl-x, p copies the path of the active panel into the console
  • Ctrl-x, Ctrl-p copies the path of the inactive panel into the console
  • Esc-Tab is auto completion

This is by far the fastest way I found so far - after I got used to the shortcuts.

2

For zsh, I find the following keybindings helpful.

# Bind alt-m to insert previous word from the current line
bindkey '^[m' copy-prev-shell-word

# Bind alt-k to kill word before cursor in vi-style
bindkey '^[k' vi-backward-kill-word

So I hit Alt-M to repeat the last argument then Alt-K if I want to delete parts off the end of a path.

Here's a useful reference to some of the behaviors you can map.

0

Use the same thing in the {,} "duplicator". I.e.:

git tag -m {v,v}1.0.1

# same as 

git tag -m v1.0.1 v1.0.1
Pylinux
  • 101
  • 2