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?

1
  • 2
    I think you mean bash-fu :)
    – cat
    Oct 13, 2016 at 11:06

5 Answers 5

42

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
3
  • damn thats sweet. I figured I wasn't the first to ask, just kind of a tricky question to word. Oct 12, 2016 at 15:21
  • What does the comma do there? Oct 12, 2016 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. Oct 12, 2016 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.

9

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█
1
  • 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, 2016 at 15:46
7

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .