Using Square Brackets in Bash: Part 1

32401

After taking a look at how curly braces ({}) work on the command line, now it’s time to tackle brackets ([]) and see how they are used in different contexts.

Globbing

The first and easiest use of square brackets is in globbing. You have probably used globbing before without knowing it. Think of all the times you have listed files of a certain type, say, you wanted to list JPEGs, but not PNGs:

ls *.jpg

Using wildcards to get all the results that fit a certain pattern is precisely what we call globbing.

In the example above, the asterisk means “zero or more characters“. There is another globbing wildcard, ?, which means “exactly one character“, so, while


ls d*k*

will list files called darkly and ducky (and dark and duck — remember * can also be zero characters),


ls d*k?

will not list darkly (or dark or duck), but it will list ducky.

Square brackets are used in globbing for sets of characters. To see what this means, make directory in which to carry out tests, cd into it and create a bunch of files like this:


touch file0{0..9}{0..9}

(If you don’t know why that works, take a look at the last installment that explains curly braces {}).

This will create files file000, file001, file002, etc., through file097, file098 and file099.

Then, to list the files in the 70s and 80s, you can do this:


ls file0[78]?

To list file022, file027, file028, file052, file057, file058, file092, file097, and file98 you can do this:


ls file0[259][278]

Of course, you can use globbing (and square brackets for sets) for more than just ls. You can use globbing with any other tool for listing, removing, moving, or copying files, although the last two may require a bit of lateral thinking.

Let’s say you want to create duplicates of files file010 through file029 and call the copies archive010, archive011, archive012, etc..

You can’t do:


cp file0[12]? archive0[12]?

Because globbing is for matching against existing files and directories and the archive… files don’t exist yet.

Doing this:


cp file0[12]? archive0[1..2][0..9]

won’t work either, because cp doesn’t let you copy many files to other many new files. Copying many files only works if you are copying them to a directory, so this:


mkdir archive

cp file0[12]? archive

would work, but it would copy the files, using their same names, into a directory called archive/. This is not what you set out to do.

However, if you look back at the article on curly braces ({}), you will remember how you can use % to lop off the end of a string contained in a variable.

Of course, there is a way you can also lop of the beginning of string contained in a variable. Instead of %, you use #.

For practice, you can try this:


myvar="Hello World"

echo Goodbye Cruel ${myvar#Hello}

It prints “Goodbye Cruel World” because #Hello gets rid of the Hello part at the beginning of the string stored in myvar.

You can use this feature alongside your globbing tools to make your archive duplicates:


for i in file0[12]?;

do

cp $i archive${i#file};

done

The first line tells the Bash interpreter that you want to loop through all the files that contain the string file0 followed by the digits 1 or 2, and then one other character, which can be anything. The second line do indicates that what follows is the instruction or list of instructions you want the interpreter to loop through.

Line 3 is where the actually copying happens, and you use the contents of the loop variable i twice: First, straight out, as the first parameter of the cp command, and then you add archive to its contents, while at the same time cutting of file. So, if i contains, say, file019


"archive" + "file019" - "file" = "archive019"

the cp line is expanded to this:


cp file019 archive019

Finally, notice how you can use the backslash to split a chain of commands over several lines for clarity.

In part two, we’ll look at more ways to use square brackets. Stay tuned.