Wildcards

Frequently, we will want to apply the same operation to multiple files. For example, we may want to move all files of a certain extension to a different folder. Rather than applying these operations one-at-a-time, most shells allow you to use a tool called a wildcard to specify multiple files at once. This allows you to set up a general template for the “type” of filename that you want to use as input (or sometimes output) to your command, and then apply the command to all files that match that template in one go.

Unfortunately, the wildcard capabilities of Command Prompt are rather limited compared to other shells. On Command Prompt, it is up to each individual command to take care of wildcards, rather than the shell taking care of it automatically (as is the case in Bash and PowerShell). However, the basic use cases for wildcards are, happily, accounted for by most of the Command Prompt commands that can take advantage of them, and so Command Prompt will be sufficient to introduce the concept. Know that what we discuss in this section is a mere taste of what is possible.

Matching all Files with a Certain Extension

Let’s consider the following situation. We have a directory that contains a variety of files of a few different types, such as the following,

We may want to create individual folders for each type of file, and move the appropriate files into the appropriate folder. Obviously, in this case, there aren’t that many files to consider. But you might well imagine a situation where we have many dozens, or even hundreds, of files that we’d like to relocate. The same principles will apply.

Let’s start by simply creating the directories we’d like to organize our files into. We’ll make one to store the C code (.c and .h), one for R code (.r), and let’s combine Python (.py) and Lua (.lua) into a directory for scripts. We can make all these new directories with one call to mkdir, like so,

Now, we can use the move command to relocate our files to the appropriate place. However, notice that we have two different .lua files. This means that we would, presumably, need to use two different commands to accomplish this task,

% move reallycomplexscript.lua scripts
% move helloworld.lua scripts

Which already seems like a lot of work–to say nothing of a more complex case having several dozen files.

This is where wildcards comes to the rescue. Rather than specifying each file individually, we can refer to them all in one go. In effect, we can issue a command that says “move everything with a .lua extension into the lua folder”. The command is,

% move *.lua scripts

The * in the command is called a wildcard. What this command says is to take all of the files that end in .lua, no matter what the rest of the filename is, and do something with them. As shown here, it has the desired effect,

The rename Command

Generally speaking, wildcards are only used to specify input files to a command. You cannot use wildcards to specify the output of a move or copy command, for example. This should make sense: wildcards need to match with existing filenames. Specifying outputs (which are often new files) would require a more advanced pattern matching system to “fill in the blanks” of the filename.

There are exceptions to this, however. Most notably, the rename command is a special case that uses wildcards for both source and destination arguments. This allows for bulk renaming of files based on some pattern, a task which cannot be conveniently done with move alone.

For example, what if we suddenly realized that we had made a mistake, and that our supposed Lua scripts were actually written in Perl, and thus ought to have a .pl extension, rather than a .lua extension? After firing the intern who no doubt made the error, how can we fix it? We could use move to rename each file individually, but this would be fairly manual. Instead, we can do it all at once using a single call to rename.

% cd scripts
% rename *.lua *.pl

As you can see, the rename command will use standard wildcarding to select its input files. For each output file, the part that matched the wildcards will be left in place, and the parts specified explicitly will be swapped. In this case, the filename itself is wildcarded on both sides, so remains unchanged. However .lua on the input side will be replaced with .pl on the output.

I won’t belabour the point here. Experiment with rename and you’ll get a feel for it.

Specifying All Files in a Directory

You can also use a wildcard to specify all files within a given directory. For example, if we decided that we wanted to pull those newly renamed Perl scripts back into our programming directory, we can do the following (with our working directory set back to the programming directory)

% move scripts\* .

Multiple Wildcards in a Single Argument

You can also include multiple wildcards in a single argument. For example, a common use case of this is manipulating files with a date or timestamp in their name, such as the following directory of jpeg image files,

We can move all of the photographs taken in November of 2020 into the appropriate directory with a single move command,

% move *_11-*-20* November-2020

It is worth taking some care with wildcards. It’s very easy to make a mistake with them. For example, the following move command may appear very similar to the one above,

% move *_*11-*20* November-2020

But, it contains a bug. Can you see what that bug might be?

Answer

The second wildcard will match all of the photographs taken in November 2020, however it will also match photographs taken on the 20th of November, regardless of the year, such as photo_11-20-19.jpg.

With move the dangers of this aren’t particularly great–just a little inconvenient most of the time–but you can imagine the havoc that could be wreaked by an incorrect wildcard in a del command.

Single-character Wildcards

There is a second type of wildcard that you can use that is a bit more restrictive than *, for when you only want to match on a single character: ?

The important distinction is simply this: the * wildcard will expand to encompass as many characters as it can. So if your wildcard is t*.txt, it will match any file which begins with a t and has a .txt extension, regardless of how many (if any) characters fall between. All of the following files will match with this wildcard,

t.txt
ta.txt
tasdf.txt
testfile.txt

A ? allows only a single character, in the same spot as the ?, to vary (and also requires that a character exists in that spot). So of the above filenames, only ta.txt would be a match for t?.txt.

You can, of course, add multiple ? wildcards (just like the *s), and mix ? and * in the same command. This can be handy if you’re doing matching on files with a very strict naming scheme and can rely on exact character counts. For example, if we wanted to select all photos above from a specific month and year, but with any prefix and any 2-digit day, we could do that with,

% move *_11-??-20.jpg November-2020

effectively forcing the shell to only let the day and prefix portions of the filename vary, while also requiring that the day portion be exactly two digits.

Conclusion

In this article, we discussed using wildcards to allow commands to process multiple files at once. Wildcards are special characters that are used to facilitate pattern matching of filenames. The * character will match with any sequence of zero-or-more characters, and the ? character will match with any single character. On Command Prompt, wildcard processing is handled by individual programs, but they are supported by the majority of standard utilities.

In the next article, we will discuss the concept of options, special arguments that can be provided to various commands to configure their behavior.

Previous
Next