2020-04-13

Bash Brace Expansion and tricks to type less on the CLI

Brace Expansion is an awesome Bash feature whose GNU documentation doesn't hype up enough.

So let's summarize a few fun things I learned while reading it being hyped up:

Braces expand like a Cartesian product:

$ echo {A,B}
A B

$ echo {A,B}{C,D}
AC AD BC BD

$ echo {A,B}{C,D}{E,F}
ACE ACF ADE ADF BCE BCF BDE BDF


But they also expand into number sequences:

$ echo {0..20}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20


You can even control the width of the numbers:

$ echo {00..20}
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20

$ echo {000..20}
000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020


And even expand into alphabetical sequences:

$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z


Combine brace expansion sequences into Cartesian products:

$ echo {A..B}{A..C}{1..2}
AA1 AA2 AB1 AB2 AC1 AC2 BA1 BA2 BB1 BB2 BC1 BC2


Brace Expansions can be used with other commands:

$ mv foo-bar.txt foo-baz.txt

can be rewritten as:

$ mv foo-{bar,baz}.txt

It just expands the braces into two arguments for the mv command.  So you could imagine using it for all kinds of other commands like tar, cp, etc.

Echo to Preview your brace expansion command:

$ echo mv foo-{bar,baz}.txt
mv foo-bar.txt foo-baz.txt


Empty Brace Expansions to add a word:

$ mv foo-bar.txt foo-bar-baz.txt

can be rewritten as:

$ mv foo-bar{,-baz}.txt

You can preview it:

$ echo mv foo-bar{,-baz}.txt
mv foo-bar.txt foo-bar-baz.txt


Want to interactively change a given file name but don't want to retype it twice?

$ mv oldname

Don't hit enter yet.  Delete (and add it to the kill ring) the last word using CTRL-W, then paste it back using CTRL-Y CTRL-Y twice to get:

$ mv oldname oldname

Now edit the second oldname until you're happy.

Of course this is useful for making copies with cp or whatever else.


Bang-hash to use an argument twice without retyping:

$ mv oldname !#:1.old

Not sure what it does?  Use the echo preview trick (but delete mv):

$ echo oldname !#:1.old
echo oldname oldname.old
oldname oldname.old

Now you see why mv needed to be deleted.  !#:1 is replaced with the first argument.

By the way, I say "bang-hash" because of calling #! "hash-bang".


Bang-hash with regex modifications
Check this out:

$ echo oldname !#:1:s/old/new
echo oldname newname
oldname newname

So instead of:

$ mv oldname newname

You can just:

$ mv oldname !#:1:s/old/new

Obviously more useful if the "oldname" was longer, or if you were doing this programatically in a bigger script.


Force glob expansion to see what * expands to:
Do your stuff:

$ mv Down*

Don't hit enter yet. Hit CTRL-x * to expand the glob:

$ mv Downloads

and carry on.

No comments: