music_knowledgeOver the weekend I was working on a playlist for a personal event coming up later this week. The playlist had about 5 hours of music--around 80 songs--that had been purchased from various sources over the years. In order to have a backup of the playlist on another machine I needed to get all of the music files in one place, so I used iTunes' "Create AAC Version" tool.

The problem with this was that many of the new files were named in the format "#{song_number} #{song_title}.m4a". For instance, "Dustland Fairytale" by The Killers was "05 Dustland Fairlytale.m4a." I could've spent my Saturday night manually clicking through and editing the filenames, but fortunately I knew that with a little bash scripting I could automate the whole process.

Rather than show the entire script right away, I want to go through the process of composing a bash script to solve this type of problem. First, we know that we can rename files using the mv command:

mv oldfilename newfilename

Next, it's important to know that you can loop through all .m4a files (or whatever other extension) in a given directory by:

for i in *.m4a
do
  [do stuff here]
done

Within the for loop we access the filename by "${i}". Your code inside the do block could be something simple like echo "${i}" or something more complicated spanning multiple lines. We can also index the filename strings in the form "${string:startindex:endindex}". If we leave off the last index it defaults to the end of the string. (You can also index from right to left, but we omit that for simplicity here.) All of the numbers I was dealing with in the playlist file were two-digits with a space separating them from the song title. So basically I wanted to drop the first three characters of the string (indices 0, 1, and 2). We can print out the shortened filenames by:

for i in *.m4a
do
  echo "${i:3}"
done

But if you do that and some of your files don't start with numbers, you will see that it chops off the beginnings of those filenames. To avoid this, we need to use an if then statement to check whether the files begin with a number. For this it is sufficient to check whether the first character of the filename is "0":

for i in *.m4a
do
	if [ "${i:0:1}" == "0" ]
	then
		echo "${i:3}"
	fi
done

We just print the modified filenames in order to check whether our script operates as intended. This is an important caveat to bash scripting--check whether your script does what you want before you run it. Bash scripts are like sharp blades: in the hands of a master they are a wonderful tool, but in the hands of an amateur they can be deadly. I'm closer to the amateur end of the spectrum so I prefer to be careful. Once we are satisfied that our little tool is only chopping off what we want it to, then we are ready to compose the whole script with that mv command:

for i in *.m4a
do
	if [ "${i:0:1}" == "0" ]
	then
		mv "${i}" "${i:3}"
	fi
done

If you put this in a script.sh file in the same directory as your playlist then you can just run bash script.sh from your terminal.

OK, so that may not have saved me that much time but it was way more fun than clicking through all those files!

the_general_problem

Note: There are probably many other ways to accomplish the same outcome described in this post. This may include other music file managers, other export methods from iTunes, or even handy bash one-liners. The point was not to give an optimal method for organizing playlists but to show the process by which a bash script evolves to solve a simple one-off task.