24

I am not sure how to word this, but I often I find myself typing commands like this:

cp /etc/prog/dir1/myconfig.yml /etc/prog/dir1/myconfig.yml.bak

I usually just type out the path twice (with tab completion) or I'll copy and paste the path with the cursor. Is there some bashfoo that makes this easier to type?

  • 2
    I think you mean bash-fu :) – cat Oct 13 '16 at 11:06
43

There are a number of tricks (there's a duplicate to be found I think), but for this I tend to do

cp /etc/prog/dir1/myconfig.yml{,.bak}

which gets expanded to your command.

This is known as brace expansion. In the form used here, the {} expression specifies a number of strings separated by commas. These "expand" the whole /etc/prog/dir1/myconfig.yml{,.bak} expression, replacing the {} part with each string in turn: the empty string, giving /etc/prog/dir1/myconfig.yml, and then .bak, giving /etc/prog/dir1/myconfig.yml.bak. The result is

cp /etc/prog/dir1/myconfig.yml /etc/prog/dir1/myconfig.yml.bak

These expressions can be nested:

echo a{b,c,d{e,f,g}}

produces

ab ac ade adf adg

There's a variant using numbers to produce sequences:

echo {1..10}

produces

1 2 3 4 5 6 7 8 9 10

and you can also specify the step:

echo {0..10..5}

produces

0 5 10
  • damn thats sweet. I figured I wasn't the first to ask, just kind of a tricky question to word. – user1028270 Oct 12 '16 at 15:21
  • What does the comma do there? – user1028270 Oct 12 '16 at 15:21
  • 5
    See Brace Expansion in the manual: it separates values used in the expansion. Here the two values are the empty string and .bak. – Stephen Kitt Oct 12 '16 at 15:22
12

History expansion can be useful for this kind of thing (assuming bash history is enabled).

In your example, you could do:

cp /etc/prog/dir1/myconfig.yml !#:1.bak

Here, the !# refers to the current line, !#:1 refers to parameter 1 on the current line,
and !#:1.bak refers to parameter 1 on the current line with '.bak' tacked onto the end.

When you have the history specifier typed out, you can use Ctrl+Alt+E to expand it to its actual value if you want to e.g. double check or modify the filename.

The "History Expansion" section in the bash man pages has more info.

8

Similar to progo's answer, but somewhat simpler: C-w cuts (“kills”) the word to the left of the cursor, C-y inserts (“yanks”) it again.

$ cp /etc/dir1/myconfig.yml█                              (C-w)
$ cp █                                                    (C-y)
$ cp /etc/dir1/myconfig.yml█                              (space, C-y)
$ cp /etc/dir1/myconfig.yml /etc/dir1/myconfig.yml█       (finish text)
$ cp /etc/dir1/myconfig.yml /etc/dir1/myconfig.yml.bak█
  • 2
    Also worth noting that you can do this with multiple words in a row (if your filename has spaces, for example) and C-y will paste all of them, as long as you haven't moved the cursor or typed anything else in between. Any mix of deletion commands except single-character deletions (e.g. backspace/del/ctrl-d) works with this. – Random832 Oct 13 '16 at 15:46
6

You can also actually cut-paste (kill-yank in emacs/bash terms) from the commandline to duplicate parts for editing.

M-d kills forwards until word boundary, and you can kill text multiple times and they all get concatenated together to a single 'paste' for yanking. Convenient!

Yank this all back to the commandline at cursor by C-y. So if you have a line like this (the block element █ indicates cursor position):

$ cp avb.txt█                 (M-b M-b)
$ cp█avb.txt                  (M-d) kill the word 'avb'
$ cp █.txt                    (C-y) yank it back
$ cp avb█.txt                 (C-e) to the end of line
$ cp avb.txt█                 (C-y) yank it again
$ cp avb.txt avb█             finish the line
$ cp avb.txt avb.foo█

Either way you prefer to do it, familiarizing yourself with what bash has to offer in interactive key department will be beneficial. http://web.mit.edu/gnu/doc/html/features_7.html

6

In tcsh or zsh, Alt+Ctrl+_ in emacs mode (copy-prev-word widget) inserts the last word, so

cp very-long-path Alt+Ctrl+_.back

bash has a copy-backward-word widget (not bound to any key by default) but that only copies the word to the ring buffer, you'd need to also press Ctrl-Y (in emacs mode) to yank it. But you could do:

bind '"\e[cpw~": copy-backward-word'
bind '"\e\C-_": "\e[cpw~\C-Y"'

To have Alt+Ctrl+_ do both the copy/snarf and paste/yank like in tcsh/zsh.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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