Changing Filename Extensions – Pattern Matching in Bash

bash1Using bash’s built-in pattern matching ability, we can write a simple script that changes filename extensions. Suppose we have a directory containing over 5000 PNG images, but all image file extensions are mislabeled as .BMP. We want all files to have the .png extension.

Here is a basic script to rename all of the files.

for file in *.BMP
   mv "$file" "${file%.BMP}.png"

This script must execute in the same directory as the image files. There are many ways to improve its functionality, but let’s focus on the core of what makes this work.


The percent part % says, “For a given filename, whatever it might be, keep everything intact up to the .BMP at the end, and replace the .BMP with .png.” Yes, it is case sensitive.

The percent character (%) used within curly braces in this manner tells bash to perform pattern matching. There are several possibilities of pattern matching and an entire chapter could be written about the subject, but for now, it is enough to know that the percent sign handles replacement at the end of a string. A single % tells bash to delete the shortest part of the string that matches the given pattern and return the rest. Whatever follows the ending curly brace will be tacked onto the end of the resulting string.

If we only perform “${file%.BMP}”, then file.BMP will return file by itself with the .BMP part stripped off. Using “${file%.bmp}.png” appends .png to the stripped-off result to produce file.png. The curly braces delimit the bounds of the variable identifier.

A Few Notes

  • Changing the filename extension does not convert the image file data from BMP to PNG.
  • Always use double quotes with bash variables in case spaces might exist in the filename.
  • If a filename contains something silly, such as file.BMP.BMP, only the last .BMP is changed to .png. So, file.BMP.BMP using the above script produces file.BMP.png.
  • Pattern matching is reportedly (according to some sources) much faster for renaming files than using rename commands or regular expressions (sed, grep), so the renaming speed is increased slightly. However, several factors can affect this.
  • Pattern matching is case sensitive.

Pattern matching is extremely useful when matching happy patterns and performing happy replacements within happier strings without the need to use elaborate regular expressions, but it can be a tricky concept to grasp because bash pattern matching is not readily advertised despite its usefulness.

The best way to grope with bash pattern matching workings is to practice using it with variables before actually renaming important files. Once the pattern is worked out, then implement it in a usable script.

Operate on Variables, not Literals

Try running these statements in a terminal. Can you guess what this returns?

echo "${var%PNGBMP}"

How about this?

echo "${var%BMPPNG}"

Can you explain why the two results are identical? Understanding how this works is a great boost to understanding pattern matching, so have fun researching. There are plenty of excellent sources available.
Just keep in mind that pattern matching MUST be performed on variables, not on literal values. In the above example, We must assign file.BMP to the variable var and operate on var.

echo "${var%BMP}"         Correct
echo "${file.BMP%BMP}"    Wrong. Using file.BMP literal as a variable that does not exist.

Remember, bash curly braces delimit variables, so whatever identifier exists within { } will be treated as a variable. If a literal string is used, such as HelloWorld, then a blank line will result in the terminal if the variable HelloWorld does not exist.
Suppose we want the string HelloWorld to read as HelloPlanet using pattern matching. Will this work?

echo "${HelloWorld%World}Planet"

Go ahead. Guess.

The answer is no. It prints valid output, but can you figure out what appears? No, it’s not an error message. Go ahead and give it a try in a terminal and see if your guess is correct.
Bash sees HelloWorld as a variable that does not exist. We need to assign the string to a variable first and then operate on the variable. Like this:

echo "${var%World}Planet"

Have fun!

, , ,

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: