Having a huge audio collection doesn’t mean anything if you can’t always find just the song or podcast you want in that moment. The roughest, but also by far the most future-proof and portable way to handle this problem, is to name all your audio files, and the folders they are in, according to one, coherent naming scheme. Here, I explain a general method to accomplish this task automatically, by means of ID3 tags.

What are ID3 tags anyway?

Here’s the definition, straight from ID3.org: ID3 is an audio file data tagging format. An ID3 tag is a data container within an MP3 audio file, stored in a prescribed format. These tags are already supported by all the MP3 players for Linux. You could also use those programs to rename your files. Here, however, we’ll see how to do it without even opening any of those tools, and you can even do it remotely. I reordered my uncle’s completely messed up music collection without leaving my home, by running the script below via SSH!

Here’s the script

    1  #! /bin/bash
    2
    3  declare -A id3tags
    4  id3tags[TITLE]="TIT2|TT2"
    5  id3tags[ALBUM]="TALB|TAL"
    6  id3tags[YEAR]="TYER"
    7  id3tags[LEAD]="TPE1|TPE2"
    8
    9  for SONG in `find $1 -type f -name "*mp3"`
   10  do
   11    rm -f /tmp/song_tags /tmp/song_info
   12    id3info $SONG > /tmp/song_info
   13    VALUE=''
   14    COMPLETE="Y"
   15    for TAG in "${!id3tags[@]}"
   16      do
   17        COMMAND="VALUE=${BACKQUOTE}egrep '${id3tags[$TAG]}' /tmp/song_info | head -1 | cut -d: -f2- ${BACKQUOTE}"
   18        eval $COMMAND
   19        if [ -z "$VALUE" ]
   20         then COMPLETE="N"
   21        else
   22           echo -n "$TAG=" >> /tmp/song_tags
   23           echo $VALUE | perl -n -e 's/\W/_/g; s/^_+//; s/_+$//; s/_+/_/g; print "$_\n"' >> /tmp/song_tags
   24        fi
   25      done
   26    if [ "$COMPLETE" == "Y" ]
   27      then
   28      echo "##################################################################################"
   29      echo "# Moving or renaming FILE: $SONG"
   30      source /tmp/song_tags
   31      # YOUR COMMAND HERE!!!
   33     fi
   34  done
   35  exit

For me, title, performer, year and album name of a song are enough to classify and rename it, but everybody is different. That’s no problem! If you want to analyze more tags with this script, just add the corresponding elements to id3tags (lines 3-7).

What is id3tags, you ask? Well, it’s an associative array — that is, a data structure whose elements are addressed via keys instead of numeric indexes: the key “YEAR”, for example, identifies the id3tags element whose value is “TYER”. Listing the tags to look at with an associative array makes it possible to add as many tags you want without changing the core part of the script.

How does the script work?

id3info is a command line utility, available as binary package for all the most common Linux distributions, that prints out all the ID3 tags it finds in a given file. The script runs id3info on all the files with mp3 extension in the directory passed as first argument (line 9), saving its output in /tmp/song_info (line 12).

The loop in lines 15 to 25 extracts from /tmp/song_info all and only the tags defined in the id3tags array. In order to do so (cfr lines 17-18), we first build on the fly the command to assign a tag’s content to $VALUE and then evaluate it. More or less, this is equivalent to running $COMMAND as if it had been written in the script from the start.

Should a tag be undefined, its length will be zero (line 19), so we’ll declare the information to be NOT complete. Otherwise, we’ll add a variable assignment command to /tmp/song_tags. To give you an idea of what I’m talking about, if running id3tags on unknown_music.mp3 produced this result (only excerpt is shown):

  *** Tag information for unknown_music.mp3
  === TIT2 (Title/songname/content description): For A Few Dollars More(1)
  === TYER (Year): 1990
  === TALB (Album/Movie/Show title): The Legendary Italian Westerns
  === TPE1 (Lead performer(s)/Soloist(s)): Ennio Morricone
then the script would write these lines into /tmp/song_tags:
  TITLE=For_A_Few_Dollars_More_1
  ALBUM=The_Legendary_Italian_Westerns
  LEAD=Ennio_Morricone
  YEAR=1990

(Note that all the non alphabetical characters were replaced by underscores by the Perl invocation of line 23)

If $COMPLETE is still “YES” after the loop, that is, if all the tags are present, we source /tmp/song_tags (line 30), to load their values inside $TITLE, $ALBUM, $LEAD and $YEAR. Which, in case you were wondering, was the main point of the script. Once we have those variables properly loaded, we can, in fact, easily build commands that rename and/or rearrange MP3 files according to whatever criteria we like best. You want your songs all inside /music/, with names as LEAD_ALBUM_TITLE_YEAR.mp3? OK: replace line 30 with:

mv $SONG /music/${LEAD}_${ALBUM}_${TITLE}_${YEAR}.mp3

You prefer nested folders? No problem! Just write in line 30 something like this:

mv $SONG /music/$LEAD/$YEAR/$ALBUM/$TITLE.mp3

Other uses (and limits) of this script

To know how many tags (that is, how many different criteria for processing) you already have inside your MP3 files, run this command, replacing COLLECTION with the absolute path to the folder where you keep them:

find COLLECTION -type f -exec id3info {} \; | grep === | cut -d: -f1 | sort | uniq

You’ll quickly see that you may use the script above as a starting point to create playlists, archives and, why not, musical culture tests, ordered by license, duration, language, popularity, label and much more.

Of course, the script won’t accomplish anything if there are no ID3 tags to work on. Could we add them to all our songs and podcasts, in the fastest possible way? Of course! Come back next week to know how!