grammar

Curly brace expansion is difficult to express in a syntactical form, but it consists of three parts, a prefix, a pair of braces, and a suffix. The contents of braces can be a comma-separated sequence of strings or a sequence of expressions.

Let’s start with an example of a comma-separated sequence of strings in braces

echo a{b,c,e}d
Copy the code

The output is

abd acd aed
Copy the code

It can be seen from the output result that {b,c,e} expands three results, namely b, C,e, and each result is combined with prefix A and suffix D.

Now let’s look at how to use a sequence of expressions inside curly braces, which has a fixed form, as follows

{x.. y[..incr]}Copy the code

X, y can be integers or a single character, x.. Y is an interval. For example, {9.. 1} represents all integers in the interval from 9 to 1, including 9 and 1. For example, {a.. F} indicates all characters in the range from a to F, including a and f.

The syntax [..incr] represents an increment, either 1 or -1 by default. For example, {9.. 1}, this curly bracket expansion does not specify an increment, but we can guess that the increment is -1. And such as {a.. F}, this curly bracket expansion also does not specify the increment, but we can also guess that the increment is 1.

However, if the default increment does not meet our requirements, we can specify the value of this increment specifically, for example

echo {1.. 7.. 2}Copy the code

The output is as follows

1 3 5 7
Copy the code

Zero padding

Let’s look at one more interesting thing, zero fill.

echo {9.. 11}Copy the code

The output is as follows

9 10 11
Copy the code

As you can see from the output, the width of each item is not the same after curly brace expansion. Sometimes when you format the output, you want each item of the result to be the same width, and then you need zero padding.

Zero padding uses the curly bracket expansion syntax

{x.. y[..inc]}Copy the code

Simply add a 0 before x or y, and the output will be filled with zero if the width is different.

Now rewrite the example above

echo {09.. 11}Copy the code

The output is

9 10 11Copy the code

From the output, each item has a width of 2, but what if I want it to have a width of 3? So let’s add another zero, and here’s the command

echo {009.. 11}Copy the code

The output is

009 010 011
Copy the code

embedded

A brace expansion can be nested inside another brace expansion, and the order of expansion is from the outside in.

Here’s a good example from the handbook

chown root /usr/{ucb/{ex,edit},lib/{ex? .? *,how_ex}}Copy the code

The outermost braces are expanded first, resulting in the following result

chown root /usr/ucb/{ex,edit} /usr/lib/{ex? .? *,how_ex}Copy the code

Then perform the curly brace expansion again

chown root /usr/ucb/ex /usr/usb/edit /usr/lib/ex? .? /usr/lib/how_exCopy the code

trap

Shell scripts are full of pitfalls, and ultimately you don’t know the syntax, so here are a few of the pitfalls I’ve encountered while writing scripts.

First, do not use curly braces in single or double quotation marks, because this expansion is not supported in single or double quotation marks.

echo "{1.. 9}"Copy the code

The output is

{1.. 9}Copy the code

Any character in a single quotation mark remains unchanged. The only special characters in double quotes are $and ‘and \. $is used for parameter expansion, command substitution, and arithmetic expansion.’ is used for command substitution.

Second, the prefix of the curly brace expansion cannot be $, because that is the form of argument expansion

echo ${1.. 3}Copy the code

The output is

-bash: ${1.. 3}: Wrong substitutionCopy the code

You can see that the system does not support the combination of parameter expansion and curly brace expansion.

Finally, if you use comma-delimited strings in braces, Spaces are not allowed in those strings

ls -ld ~/{make_ws, shell_ws}
Copy the code

The execution result is

Ls: can't access '/home/david/{make_ws,': no that file or directory ls: can't access 'shell_ws}': no that file or directoryCopy the code

I don’t know if you see why that’s the case? Since Spaces appear after the first comma, the shell takes ~/{make_ws and shell_ws} as arguments to the ls command!

If we remove this space, the shell will take the whole ~/{make_ws, shell_ws} as arguments to the ls command, and the shell will perform two expansions: curly brace expansion, wavy line expansion, and finally the result of the expansion as arguments to the ls command.

So let’s correct the incorrect command again. The correct command is as follows

ls -ld ~/{make_ws,shell_ws}
Copy the code

The output is

Drwxrwxr-x 3 David David 4096 6月 25 16:01 /home/david/make_ws drwxrwxr-x 2 David David 4096 7月 18 08:07 /home/david/shell_wsCopy the code

example

To take a classic example at work, I have an image called Demo.png and now I want to copy the demo.png image into 37 images named 000.png, 001.png,… , 037 PNG.

My first thought was to run the cp command 38 times, as follows

cp -v demo.png 000.png
cp -v demo.png 001.png
...
cp -v demo.png 037.png
Copy the code

This is crazy, you should feel like you’re doing the same thing over and over again, so I immediately thought of using a script command to do this, as follows

for name in {000.. 37}; do cp -v demo.png ${name}.png; doneCopy the code

This command uses curly braces to expand {000.. 37}, each expanded item has a width of 3, for example, 001.

priority

There are many types of expansion in the shell, but curly braces have the highest priority. If you encounter mixed shell expansions and are unsure of the order of expansions, consult the Bash manual.

reference

www.gnu.org/software/ba…