mass renaming files with complex filenames

Hi,

I've got files with names like this :
[Various-PC3]_Some_Name_178_HD_[e2813be1].mp4
[TTB]_Some_Name_-_496_Vost_SD_(720x400_XviD_MP3).avi
Goffytofansub_Some name 483_HD.avi

And iam trying to rename it with a regular pattern. My gola is this :
Ep 178.mp4
Ep 496.avi
Ep 483.avi

I've tried using sed with substitution expressions :

 for f in `ls`
do
        OLD=$f
        NEW=$(echo $f | sed 's/.*\([0-9]\{3\}\).*\.\(.*\)/Ep \1.\2/1')
        echo -n "$OLD => $NEW (o/n) "
        read YES_NO
        [[ $YES_NO == "y" ]] && mv $OLD $NEW
done

the result is

Ep 813.mp4
Ep 400.avi
Ep 483.avi

One file correctly renamed :-/

This one :

 echo $f | sed 's/[^0-9]*\([0-9]\{3\}\).*\.\(.*\)/Ep \1.\2/1'

Went wrong with the first one

[Various-PC3Ep 178.mp4
Ep 496.avi
Ep 483.avi

Then I tried perl. But considering that I have very few knowledges in it, I spent many time on my script

open(LS, "ls -1 |");
foreach (<LS>)
{
        $old = $_;
        $new = s/(?![0-9]{3})*([0-9]{3}).*\.(.*)/Ep $1.$2/;
        print "$old => $new (o/n) ";
        chomp($yes_no = <STDIN>);
        next unless ( $yes_no == "y");
        system("mv $old $new");
} #foreach

for an unexpected result :

Ep 418.mp4
 => 1 (o/n) <- waiting here for input

I've also tried some alternatives of the beginning :

foreach(`ls`)

result

Ep 418.mp4
  => 1 (o/n) <- waiting here for input

This one :

foreach (system("ls -1"))

gave me

 Various-PC3]_Some_Name_178_HD_[e2813be1].mp4
[TTB]_Some_Name_-_496_Vost_SD_(720x400_XviD_MP3).avi
Goffytofansub_Some name 483_HD.avi
0 =>  (o/n) <- waiting here for input

Any idea on how to reach my goal ?

Thx in advance !

You want more of an assembly line, not command calls for one file:

 
export zct=$start_no
find $top-dir -type f -name '*[a-zA-Z].[am][vp][i3]' | sed '
  /avi$/!{
    /mp3$/!d
   }
 /avi$/{
    /MP3/!d
   }
  s/.*/mv "&" $(( zct += 1 )).mp3/
 ' >/tmp/mvall.sh

Review before executing! You might take all the files for one dir and do them at once in the background (parallel), just wrap lines for each dir in parens with amp: (...)& More than one process renaming in the same dir makes for file contention.

EDIT: DGPickett beat me to it.

I don't know the answer to your question/problem, but in my experience, using 'ls' with a 'for' statement is just bad practice and even more so with spaces in your filenames. I would direct you to use the 'find' command.

Try replacing your sed by:

sed -e 's/\([0-9]\{3\}\).*\(\..*\)/\1\2/' -e 's/.*\([0-9]\{3\}\)/Ep \1/'
1 Like

Oh, extracted numbers! Sure. I never use -e on sed, just pile the lines on the command line, but with pretty spacing, sed on sed lines and shell on shell lines. You, too, deserve readable code and a lower error rate!

Still a lot of mv fork() and exec(), so PERL might have an advantage, assuming it knows how to rewrite directories internally. In C or PERL, you link() the file to the new name and unlink() it from the old name.

BTW, directories never shrink, so making new directories is not a bad idea. Big directories can waste a lot of time.

I strongly advise that you hide these filenames from Shell and avoid trying to put them into Shell environment variables. Try to work with external commands only.

Use unix "find" to generate a list of filenames into a file.

Then create a "sed" file containing your "sed" commands. Because Shell will not see them we only need to escape characters where "sed" requires it (which it will with some of these filenames!).

Run with the outline syntax:

sed -f sed_scriptfile filename

Throughout this exercise we must avoid Shell seeing the filenames.

Quite the contrary.

You want the shell to see the filenames, not the individual words making up those names.

The correct way is:

for file in *
do
  : whatever
done
1 Like
$ awk -F"[_. ]" '{for (i=1;i<NF;i++) {if ($i~/^[0-9]*$/) name=$i}}; {print "Ep " name "." $NF;name=""}' infile

Ep 178.mp4
Ep 496.avi
Ep 483.avi
1 Like

You all are really impressive !

Thanks very much for your answers I found the answer to my question and much more !

@AlphaLexman : concerning the use of "ls" in a "for" statement inside a shell (bash in my case) script, I resolved the spaces issue by setting the IFS variable to
IFS="
"
every beginning of my scripts. I didn't mention it in my first post ... I don't know why !

Today this seems not necessary anymore thanks to cfajohnson who learned me correct way to list files within a script.
So simple but really efficient !

Coming back to my initial question, I'll go on with Shell_Life's sed expression which works like a charm.

Anyway I'll keep all the other suggestions in my bag, they are all interesting.

Bye