Mastering Globbing in Zsh: An In-Depth Guide


The Z shell, or zsh, is a powerful Unix shell known for its enhancements over traditional shells like bash or sh. One of the areas where zsh truly shines is its advanced file globbing features. In simple terms, globbing refers to the use of special characters to match filenames or directory names based on specific patterns.

In this article we will be working with some example files. At the end of this article is a section on Setting Up Example Files for Testing.

Understanding Globbing

Wildcards (* and ?)

Globbing is a feature provided by Unix-based shells that allows you to select filenames based on wildcard matches, making it easier to select multiple files at once. A few examples of wildcards include the asterisk (*), which represents any number of characters (including none), and the question mark (?), which represents a single character.

ls *.txt

In this example, the ls command will list all files in the current directory that end with .txt.

Ranges ([])

Zsh allows the use of ranges in its globbing. For example:

ls file[1-3].txt

This command will match file1.txt, file2.txt, and file3.txt.

Within a range, you can also specify a range of characters. For example:

ls file[a-g].txt

However, ranges are limited to single digits or character. For instance, [10-15] won’t match numbers between 10 and 15 as one might expect. Instead, it will match any single character that is 1, 0, or between 1 and 5.

Ranges with Comma Separation ([])

Ranges in Zsh can be broken up using commas, allowing for more specific globbing patterns. The range operator [ ] matches any single character within the brackets. While a dash - specifies a range of characters, a comma , allows you to define multiple, non-sequential characters or ranges.

For example:

ls file[1-3,5,7-9].txt

In this pattern, the range 1-3 will match any single digit from 1 to 3, 5 will only match the digit 5, and 7-9 will match any single digit from 7 to 9. Therefore, this command will match files such as file1.txt, file2.txt, file3.txt, file5.txt, file7.txt, file8.txt, and file9.txt, but not file4.txt or file6.txt.

Here’s another example that combines characters and digits:

ls file[a,c,e,1-3].txt

This command will match filea.txt, filec.txt, filee.txt, file1.txt, file2.txt, and file3.txt.

Using commas to break up ranges provides greater flexibility in defining your globbing patterns, allowing for precise matching of file and directory names.

Combining Ranges and Wildcards

In Zsh’s globbing, ranges and wildcards can be combined to create more intricate patterns. This allows you to match very specific sets of files or directories, based on your requirements. Let’s look at some examples:

1. Using Asterisks (*) with Ranges ([])

The * wildcard character represents any string of characters, while the range [] matches any character within the brackets. Together, they can be used to match files that contain certain characters in a specific position.

ls *[1-5].*

In this example, the command will match any file that has a number between 1 and 5 right before its extension, such as file12.txt, data4.csv, doc2.md, etc.

2. Using Question Marks (?) with Ranges ([])

The ? wildcard represents any single character. When combined with a range, it allows for matching filenames that have specific characters at certain positions.

ls file?[1-5].txt

This command will match any file that starts with file, any character, and then followed by a single character that is a number from 1 to 5, and ends with .txt. So, it would match files like file11.txt, file13.txt, but not file10.txt or file.txt.

3. Combining Multiple Ranges ([])

You can even use multiple ranges within a single glob to match files based on multiple criteria:

ls [a-c]*[1-3].txt

This command will match any .txt file that starts with either a, b, or c, and ends with a number between 1 and 3, such as a_file1.txt, b2.txt, c_something3.txt, etc.

By combining ranges and wildcards, you can create powerful file matching patterns that can be highly customized to your needs, making your command line experience more efficient and effective.

Single and Double Inclusion (? and **)

While ? matches any single character, ** matches any string of characters across directory boundaries. For instance:

ls **/*.txt

This command will recursively find all .txt files in the current directory and all subdirectories.

Numeric Globbing (<->)

Zsh provides an easy way to match numbers within a range using <->:

ls file<1-3>.txt

This command is equivalent to file[1-3].txt and matches file1.txt, file2.txt, and file3.txt.

Expanded Numeric Globbing (<->)

Numeric globbing in Zsh is not just restricted to a defined range. It also allows you to specify only a lower limit, an upper limit, or no limit at all, granting flexibility when dealing with numbers in filenames.

For example, <10-> will match any number starting from 10. Consider the following usage:

ls file<10->.txt

This command will match all files that start with file and end with a number that is 10 or greater, followed by .txt. So, files like file10.txt, file11.txt, file20.txt, and file100.txt would match this pattern.

Similarly, you can specify an upper limit without defining a lower limit. For example, <-10>will match any number up to 10:

ls file<-10>.txt

This command will match all files that start with file and end with a number that is 10 or less, followed by .txt. Hence, files like file1.txt, file2.txt, file9.txt, and file10.txt would match this pattern.

If you don’t specify any limit, <-> will match any number:

ls file<->.txt

This command will match all files that start with file and end with any number, followed by .txt.

It’s important to note that, unlike the range [ ], numeric globbing is exclusive to numbers. It can’t be used with letters or other characters. This specialization, however, makes numeric globbing a powerful tool for handling numeric sequences in file operations.

Pattern Lists (|)

You can match any of several patterns separated by | within parentheses:

ls *.(txt|doc)

This command will list all files in the current directory that end with .txt or .doc

Using Braces {} in Zsh

Brace expansion is a powerful feature in Zsh that allows you to generate arbitrary strings. It’s useful in many situations, such as creating multiple directories or files at once or renaming files.

Let’s dive into some examples:

1. Creating Files

Brace expansion can be used with the touch command to create multiple files at once. For instance:

touch file{1..5}.txt

This command will create five files: file1.txt, file2.txt, file3.txt, file4.txt, and file5.txt.

2. Creating Directories

Similarly, you can create multiple directories using the mkdir command:

mkdir dir{1..5}

This command will create five directories: dir1, dir2, dir3, dir4, and dir5.

3. Combinations and Sequences

Braces can be used to create combinations or sequences of strings. This is especially useful for creating a series of files or directories:

touch file{A,B}{1..3}.txt

This command will create six files: fileA1.txt, fileA2.txt, fileA3.txt, fileB1.txt, fileB2.txt, and fileB3.txt.

4. Brace Expansion with Letters

You can also create a sequence of letters:

echo {a..f}

This command will output: a b c d e f.

Differences Between Braces {} and Ranges [] in Zsh

Brace expansion in Zsh allows for the generation of arbitrary strings and is similar to the range operator in some aspects. However, a crucial distinction exists when applying these in the context of file operations.

The similarity between braces and range operators comes to the forefront when you’re creating sequences. For example, {1..5} and [1-5] will both generate sequences from 1 to 5. The same holds true for alphabetic sequences; {a..e} and [a-e] will both generate sequences from ‘a’ to ‘e’.

Now, let’s examine the difference. If you’re trying to match existing files using these sequences, brace expansion {} and range operator [] behave differently.

When using a range, Zsh interprets it as a pattern to match against existing filenames. If no files match the pattern, the command will execute without any matches. For example, if you run ls file[1-5].txt but only file1.txt and file5.txt exist, the command will only list those two files.

Conversely, brace expansion in Zsh does not check if files exist. Instead, it generates the entire sequence and passes each element to the command. If a file doesn’t exist, Zsh will still attempt to pass it to the command, which can result in an error.

For example, if you run ls file{1..5}.txt but only file1.txt and file5.txt exist, Zsh will still attempt to list file2.txt, file3.txt, and file4.txt. As these files don’t exist, the ls command will return an error for each nonexistent file.

While braces {} and the range operator [] both allow you to generate sequences, their behavior differs when used for file operations. The range operator is more forgiving as it only matches existing files, while brace expansion requires precise knowledge of the files present in the directory. This distinction emphasizes the importance of understanding the nuances of each tool for efficient and error-free operation in Zsh.

Setting Up Example Files for Testing

For a hands-on experience with the globbing examples provided, it may be beneficial to create a set of sample files. These files will serve as a playground for you to explore and test the various Zsh globbing functionalities. Here’s how you can set this up:

First, let’s create a new directory named files in your home directory. This dedicated space will help keep the test files separate, facilitating easy clean-up once you’re done experimenting.

mkdir ~/files

Next, navigate into the newly created files directory:

cd ~/files

Now, let’s generate the sample files. We’ll create a series of text files named file1.txt through file20.txt and filea.txt through filez.txt.

touch file{1..20,a..z}.txt

With this, your files directory is now populated with a diverse set of files that you can use to experiment with Zsh globbing.

Once you’re done with your exploration, you can easily clean up these test files and the files directory. It’s as simple as running the following command from your home directory:

rm -r ~/files

This setup allows you to immerse yourself in learning Zsh globbing, providing a safe and straightforward way to experiment and master the discussed concepts.

Conclusion

Zsh’s extended globbing capabilities bring a level of sophistication and precision to the process of working with files and directories. These globbing features can drastically simplify complex operations and reduce the need for external tools like find or grep. While the syntax may seem daunting at first, the time invested in mastering these techniques will pay dividends in your day-to-day command line usage.

Daniel

Whilst building web applications, Daniel also sets up web servers from scratch because he has yet to find the perfect hosting solution. His philosophy is “Why settle, when you can build it better yourself?”

Recent Posts