Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

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%\ (*)}
year=${album_year:-5:4}

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:

year=${album_year:$((${#album}+2)):$((${#album_year}-${#album}-3}

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"
album_year=${segments[-2]}
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
1  
@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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .