Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a list of mp3 files named like so:

$artist/$album ($year)/$tracknum $title.mp3

Many of these tracks have incorrect or missing tags, which I am correcting through the use of id3v2 and regex because the file names are all correct. Here is an example of how I'm setting titles:

for mp3 in *.mp3; do id3v2 -t "$(echo $mp3 | cut -d. -f1 | cut -d\  -f2-)" "$mp3"; done

(Note: quotes are there because the titles often have spaces, which causes bash to complain unless spaces are used)

My question is this: is there a better way to extract a specific substring from these files? In the case of the titles, I can't use grep -o without also matching $tracknum because many tracks have numbers and spaces in the titles (meaning that I'd need "([0-9a-zA-Z ]+)" for my pattern, which matches the entire filename before the .mp3).

I'm almost certain that there is a much better way to do this. Is there?

share|improve this question
up vote 1 down vote accepted

If you want to extract the title (and, in case it's useful, the tracknum), you should be able to do it like this:

read -r tracknum title <<<$(basename "$mp3" .mp3)

For the other bits, I'd start by splitting the filename by / to isolate the parts. For example:

album_year=$(cut -d/ -f2 <<<"$mp3")
album=${album%\ (*)}

The last one is dependent on the year actually being present and four digits long; if that's not guaranteed then you could do it using a more clumsy expression:


Also, if you don't know how deep in the path the album_year part is, you can extract the second last segment (i.e. the immediate directory of the file) with:

album_year=$(basename "$(dirname "$mp3")")

Or, you can split the entire path into an array:

IFS=/ read -ra segments <<<"$mp3"
share|improve this answer
I had to reverse the path string (it's the complete path from /) before passing it to cut, but this was overall very helpful. – J David Smith Aug 5 '13 at 21:59
@J.DavidSmith: You could have just adjusted the -f2 to the actual depth :) I added another way to get the second last component of a filepath. – rici Aug 5 '13 at 23:39
If I could upvote your answer again I would. – J David Smith Aug 5 '13 at 23:59

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.