LinuxQuestions.org
Review your favorite Linux distribution.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-19-2004, 11:02 AM   #1
Nice-n-Slow
LQ Newbie
 
Registered: May 2004
Posts: 24

Rep: Reputation: 15
Reorganizing file names


Lets say I have a big list of filenames that are organized like this.

Title by Artist [Album].mp3

So, a fictitious example might be

Rockin' Holiday by Permanent Vacations(The) [Wish You Were Here].mp3

But I want to change the organization to

Title - Album - Artist.mp3

So, the fictitious example would look like

Permanent Vacations(The) - Wish You Were Here - Rockin'Holiday.mp3


How would I do that in a shell script? I'm interested in the specific example, but I'm also even more interested in the basic mechanics behind it. And by mechanics I mean the basics. I'm really hoping someone could help me to understand how to decide which variable get assigned to do what so I could modify it to suit another situation.
Thanks


PS, I am reposting this from the shell script forum at LinuxBeginner.org where it sat for days until I noticed that the next most recent post was from September 2003.
 
Old 05-19-2004, 11:59 AM   #2
The_Nerd
Member
 
Registered: Aug 2002
Distribution: Debian
Posts: 540

Rep: Reputation: 32
Though I am no good (or not very) at shell scripts, I want to suggest a few way you could do this:

(1): with your program, use command line args for title artist and what ever else, then if the filename contains these args save it as what you want... however I am not quite sure how to get the song name. You could just use what ever is NOT matched from the args.

(2): If you find the mp3 format, it contains the artist, title, etc... in the file, then all you would have to do is open it, get the info, and rename the file acordingly

Hope that helps!
 
Old 05-19-2004, 12:18 PM   #3
bahramH
Member
 
Registered: Apr 2004
Location: France
Distribution: Mandrake
Posts: 67

Rep: Reputation: 15
pattern recognition

Well, the example you're citing is not the easiest one. Each shell uses as set of modifiers to choose part of a word. Let say you have a varible a which contains foo.jpg. I.e typing
> echo $a
gives you
> foo.jpg

Then in tcsh, $a:r gives you the root name without the extension :
>echo $a:r
>foo

You have all kind of modifiers, and you can get the lot of them from "man csh". Here a
preview :

h : Remove a trailing pathname component, leaving the head.
t : Remove all leading pathname components, leaving the tail.
r : Remove a filename extension `.xxx', leaving the root name.
e : Remove all but the extension.
u : Uppercase the first lowercase letter.
l : Lowercase the first uppercase letter.
s/l/r/ : Substitute l for r. l is simply a string like r, not a
regular expression as in the eponymous ed(1) command. Any
character may be used as the delimiter in place of `/'; a
`\' can be used to quote the delimiter inside l and r. The
character `&' in the r is replaced by l; `\' also quotes
`&'. If l is empty (``''), the l from a previous substitu-
tion or the s from a previous `?s?' event specification is
used. The trailing delimiter may be omitted if it is imme-
diately followed by a newline.

Now, if you want to change all the extension from jpg to tiff, in "sh" you would write

a = `ls *jpg` // get you all the files with jpeg extension and put to variable a

fore file in $a
do
mv $file $file:r.tiff
done


This is shell programming. For complicated stuff, better to use perl or plain C.
 
Old 05-19-2004, 02:40 PM   #4
Nice-n-Slow
LQ Newbie
 
Registered: May 2004
Posts: 24

Original Poster
Rep: Reputation: 15
First for The_Nerd
I appreciate your attempt to help with a shortcut, but in this case there are no id3 tags. I'm hoping to re-organize the names in part so that I can later rebuild the id3 tags. Getting the file names cleaned up is the first step towards rebuilding the tags. It was a good suggestion though.

Now for bahramH
Hmm, you're already giving me some good stuff I didn't know about shell programming. Thanks. That's what I was interested in as much as the answer itself.
So, are you seem to lean towards the idea that this is a job for perl? That works out somewhat nicely as this forum is open to all sorts of languages. Perhaps I could get some perl tips too. I don't insist it be done as a BASH script. I thought that would be the most convenient way to go, but perl would be good too.

And although I don't have any shell scripting or perl background, I do have some programming skills. In fact I've done a lot of work in a variation of Pascal, so I'm comfortable with loops and counters and calling functions. In fact, I am fairly confident I could achieve my goal using some old Windows tools that I'm already familiar with. So, achieving the goal per-se isn't really my main target. I'd really like to learn how to do it under Linux and get some tips on how to do it right. My example may not be the simplest possible case, but it's shouldn't be a terribly difficult task. Renaming files seems like a fairly mundane admin type job.
I will be gone for a day, so I won't be able to check for replies for another twenty four hours or so. But I hope this thread can see a little more action.
Thanks for what has been put forth so far.
 
Old 05-19-2004, 03:14 PM   #5
320mb
Senior Member
 
Registered: Nov 2002
Location: pikes peak
Distribution: Slackware, LFS
Posts: 2,577

Rep: Reputation: 48
the awk/gawk program may be what your looking for

this is what I use gawk for.......I have a bunch of .ogg files, I wrote a script to use gawk to look thru them and choose a song a random and play it
thru mplayer.......LOL
Code:
#!/bin/bash
#random-ogg: play a random file from jukebox
cd /linux/
filename=$(find -type f | 
     gawk 'BEGIN{ 
     srand() 
}
{     
     names[NR]=$0
}
      END{ i=1+int(rand()*NR)
      print names[i]
} 
')
echo "$filename"
mplayer "$filename"
gawk can do a whole lot more.....read the man page!!!!
 
Old 05-19-2004, 04:11 PM   #6
jschiwal
LQ Guru
 
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733

Rep: Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682
Both types of filenames use some characters that have special meaning to the shell. So its best to test a script. Where things need quoting or backslashing can be easy to get wrong at first. Give this script a try. Read the info page for bash. Do a search for 'variable substitution'.

${file%<pattern>*} will remove the pattern and everything after the pattern. For example ${file%.mp3} will remove the .mp3 extension from the filenames. So if you were using a program that converted the .mp3 files to .ogg files, the output filename in your script would be "${file%.mp3}.ogg".

If there could be any spaces in the filenames, you have to use quotes: "${file}" Some special characters, such as '!', have to be backslashed, so they don't perform some function ( ! executes the last command, ( starts a subshell.
Code:
for file  in "* by *[*].mp3"
  titleartist="${file%[*}"
  artist="${titleartist#* by }"
  album1="${file#*[}"
  album="${album1%]*}"
  newname="${title} - ${album} - ${artist}.mp3"
  echo $newname
done

Last edited by jschiwal; 05-19-2004 at 04:13 PM.
 
Old 05-20-2004, 05:16 AM   #7
jschiwal
LQ Guru
 
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733

Rep: Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682
I wasn't at my home computer when I made my first response, so I wasn't able to test my short script. There was a couple problems with it. Here is the revised script:
Code:
#!/bin/bash
for file  in *\ by\ *\ \[*\].mp3; do
    titleartist="${file%[*}"
    title="${titleartist% by *}"
    artist="${titleartist#* by }"
    album1="${file#*[}"
    album="${album1%]*}"
    newname="${title} - ${album} - ${artist}.mp3"
    echo -e "newname: " $newname "\n"
    mv "${filename}" "${newfilename}"
done
test of script:
$ ls *.mp3
Wandering Road by Simsons [Wanderings].mp3 Yellow Submarine by Beatles [He
Yellow Brick Road,The by Munchkins [Wizard of Oz].mp3
$ ls *.mp3
Wandering Road by Simsons [Wanderings].mp3
Yellow Brick Road,The by Munchkins [Wizard of Oz].mp3
Yellow Submarine by Beatles [Help].mp3
$ ./tst
newname: Wandering Road - Wanderings - Simsons .mp3

newname: Yellow Brick Road,The - Wizard of Oz - Munchkins .mp3

newname: Yellow Submarine - Help - Beatles .mp3

$ ls *.mp3
Wandering Road - Wanderings - Simsons .mp3
Yellow Brick Road,The - Wizard of Oz - Munchkins .mp3
Yellow Submarine - Help - Beatles .mp3
 
Old 05-20-2004, 03:20 PM   #8
The_Nerd
Member
 
Registered: Aug 2002
Distribution: Debian
Posts: 540

Rep: Reputation: 32
Cool!
 
Old 05-21-2004, 03:01 PM   #9
Nice-n-Slow
LQ Newbie
 
Registered: May 2004
Posts: 24

Original Poster
Rep: Reputation: 15
Back from the mad trip. I totally blew it. I took a two hour trip to go to a friend's going away party and found out I went on the wrong day. Not too cool.
But life is tough sometimes.

Back to the topic at hand. I'd like to introduce two questions in order of importance.

First, in the script. I see the hash, pound or as I like to call it tic-tac-toe sign appear on the third line

Code:
for file  in "* by *[*].mp3"
  titleartist="${file%[*}"
  artist="${titleartist#* by }"
What is the purpose of the hash symbol here?

I should note that I did, indeed, look up the bash info pages as well as the man pages and I didn't have much luck finding any sections on variable substitution although I willingly confess I'm not a super info user.

I did, however, look in the Advanced Bash Scripting Guide and Linux in a Nutshell. The former suggested the hash was for comments, the latter contained a section on variable substitution that gave a meaning for $#, but didn't seem to properly define the example you gave.
So, if you please, what is the meaning of that hash sign?

Secondly, and less importantly, I ran the script and had an error.

My terminal returned the following bit of text:

mv: cannot stat `': No such file or directory

I copied the script into an emacs editor and saved it, named it and copied it into the same directory as the target files and I'm running it as root. So, I think I've got most of the ingredients for success, but it's not quite working yet.

I'd like to emphasize though that the fact that it's not working so far is less important than knowing a bit more about how the script works because perhaps if I could more thoroughly understand the script I could solve my own problem. As I mentioned in the beginning, the solution really isn't the most important part.
 
Old 05-23-2004, 12:16 PM   #10
Nice-n-Slow
LQ Newbie
 
Registered: May 2004
Posts: 24

Original Poster
Rep: Reputation: 15
Another syntax question.
In the following line


Code:
 echo -e "newname: " $newname "\n"
What is the purpose of the colon ":" in "newname:"
 
Old 05-24-2004, 04:09 AM   #11
jschiwal
LQ Guru
 
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733

Rep: Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682
The # sign is used in variable substitution. It is like the % sign but is used to remove from the beginning rather then the end.
${file#*.} will remove from $file, everything upto the first '.' dot.
${file##*.} will remove from the variable $file, everything upto the last dot.
So if ${file} is a typical filename like 'my-fav-pic.jpg', then ${file##*.} will match the extension "jpg". Notice the the dot is deleted also.
for file in *.JPG; do mv "${file}" "${file%.JPG}.jpg"; done
will rename all files with a JPG extension to jpg.

As to you second question, the colon does nothing. I was just displaying the string 'newname: ' before the filename.

That entire line isn't needed. However, I like to identify items when I write a script, just to verify that is is doing what I think it would.

One final hint. You might consider using the shell option 'nullglob'
From the bash manpage under shopt options:
`nullglob'
If set, Bash allows filename patterns which match no files to
expand to a null string, rather than themselves.

this means that if not a single file matches the search pattern, then $file becomes null rather than the string '* by *[*].mp3'
 
Old 05-24-2004, 05:51 AM   #12
Nice-n-Slow
LQ Newbie
 
Registered: May 2004
Posts: 24

Original Poster
Rep: Reputation: 15
Hey, I figured out the shopt part anyhow.

For anybody curious it was
shopt -s nullglob

Feeling quite accomplished.
And I'm starting to think I'm following the script. I never did find anything about bash variable substitution on my local info system, but I did find some stuff about it on the web and that helped.

But although I've played with the last few lines quite a bit and set nullglob to on, I'm still getting the echo of the newname and then the error

mv: cannot stat `': No such file or directory

I put a simple test file in the directory and renamed it using mv successfully. But perhaps the example was too simple. I just used text.txt to changed.txt. That wasn't a problem, but perhaps it has something to do with all the spaces. Maybe I'll try a more complicated mv test and see if I can duplicate the error.
Does the `': part of the error suggest anything specific about the script, or is that just a typical error message?
 
Old 05-24-2004, 08:07 AM   #13
jschiwal
LQ Guru
 
Registered: Aug 2001
Location: Fargo, ND
Distribution: SuSE AMD64
Posts: 15,733

Rep: Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682Reputation: 682
Keep in mind that the variables have to be enclosed by double quotes if it is possible that the variables might contain any white space. Single quotes won't work, because the variables won't be expanded then.
Also, be careful that you filter the variable to contain only the types of file patterns that you want. For example, if the files you want to alter use the pattern picturename_##.jpg, then a pattern you could use would be,
for file in picturename_[[:digit:]][[:digit:]].jpg; ...
 
Old 05-24-2004, 10:21 AM   #14
Nice-n-Slow
LQ Newbie
 
Registered: May 2004
Posts: 24

Original Poster
Rep: Reputation: 15
How very amsuing.
Well, the best hint was the reminder that echo was giving me a clue as to what was going on, so all I had to do was echo a few of the other variables to see what I was working with.
Turned out that a few of those variables at the end were more figurative than literal. After echoing them to see what values they were holding, I changed filename to file and newfilename to newfile and presto bingo ala peanut butter sandwiches.
Sweet, and it does the job fast too.
Thanks man. That was good and I really learned a few things.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Unicode file names Ygrex Linux - Software 2 10-28-2005 04:30 PM
Reorganizing partitions help eyebrowsoffire Linux - General 3 02-18-2005 09:40 AM
xmms file names gazza Linux - Software 3 11-18-2003 03:50 PM
duplicate file names? BajaNick Linux - Software 3 08-25-2003 04:00 PM
inaccessible file names pikachu66 Linux - General 2 10-01-2001 12:24 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:25 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration