move 7500 files to various directories

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

Threaded View
Let me say up front that this is my first software writing experience.
 As of a week ago, I had no knowledge of perl or any other language.
Over the past weekend, I've done a ton of searching and reading.  I
believe my problem is simple, but the solution isn't quite as easy for
me to write.

My problem:

I have 7500 pictures in a directory that need to move to roughly 40
different directories.  I have a text file that lists the names of
7500 files (digital pictures) and the corresponding directory they
need to be moved to.  All the files are currently in a single
directory, c:\photos. The filenames do not contain spaces, but the
directory names do.  I'm also working in windows XP with activestate
installed. If needed I could transfer everything to a linux machine.

Sample from album.txt:
picture1.jpg c:\album02-12 christmas at home
picture2.jpg c:\album02-07 july 4th in DC

From what I gather, my path forward is to read the keys and values
from the text file into a hash, and then for each key/value combo
issue a rename.  I've pulled samples from the net and my Learning Perl
book to come up with the code listed below.

Could someone look over what I have so far and point out what's wrong
(I haven't tried it yet, but I'm guessing that something is wrong -
like the split command picking up the spaces in the directory names,
and my command for renaming using the keys and values).  A corrected
version would very much appreciated.

Thanks for your help,

# This script moves pictures from a flat directory
# to various directory locations

use strict;
use warnings;

# album.txt is 7500 lines of filename.jpg [space] c:\dirname\
$album_list = "C:/photos/album.txt";
open LISTING, $album_list or die "can't read $album_list: $!";

### Read data from txt file into hash

# read file into hash
chomp(my %hash = map { split / / } <LISTING>);

# verify
print map { "$_ => $hash\n" } keys %hash;

### Move files

while ( ($key, $value) = each %hash) {
    rename $key , $value . $key;

Re: move 7500 files to various directories

Quoted text here. Click to load it

That won't be necessary.

Quoted text here. Click to load it

The hash isn't strictly necessary.  You could read each line from the
album file and process it as it comes.

If you wanted to make preliminary checks (Do the image files exist on disk?
Are the target directories already created?) the hash may be a good idea,
but your code doesn't do that.

Quoted text here. Click to load it

You should, normally.  You may be able to iron out some wrinkles
before presenting the code to the group.  Test runs can also bring
up specific questions you might not have thought of otherwise.

Quoted text here. Click to load it

That's repairable.

The command looks fine, but like every external command the result
should be checked and errors reported.

Quoted text here. Click to load it

Strict and warnings.  Good.

Quoted text here. Click to load it

As you anticipated, "split / /" would split the directory names that
contain blanks, which would royally mess up the structure of the
hash list.  Give split() a limit of 2.

Also, the chomp won't work as intended (or maybe it will, but for the
wrong reason).  You can chomp the values.

    my %hash = map { split ' ', $_, 2 } <LISTING>;
    chomp values %hash;

Quoted text here. Click to load it

Well, that prints it out, but doesn't give you a chance to do anything
if you find something is wrong.  It's hard to see what it's good
for in the finished program, but a preliminary version should certainly
do this at some point.

Quoted text here. Click to load it

"$key" and "$value" don't tell the reader anything about the purpose
of the variables.  "$file" and "$target_dir" would be more specific,
"$photo" and "$album" still more.  You forgot to declare the variables
(necessary under strict).  You'll need a directory separator between
the file and the directory name, just concatenation won't do. Further,
rename() should be checked for errors:

    while ( my ( $photo, $album) = each %hash ) {
        rename $photo, $album . '/' . $photo or
            warn "Can't move $photo to $album: $!";

There's probably more, I haven't run the program either.  In any case,
before you run it on actual data, be sure you can restore the original
state.  If it runs wild, recovering from the mess can be hard.  "rename"
can even lose data.


Re: move 7500 files to various directories

Well it is good that you used strict and warnings but it is a pity that you did
not run your own code. It does not compile as $album_list, $key and $value have
not been declared.

You really need to present working code otherwise people will just ignore you.

Putting in the mys and running it we get.

Odd number of elements in hash assignment at x1 line 18, <LISTING> line 2.
Use of uninitialized value in concatenation (.) or string at x1 line 21,
<LISTING> line 2.
  => picture2.jpg
4th => in
picture1.jpg => c:\album02-12
c:\album02-07 => july
christmas => at

The problem is the split. It is splitting up the line on every space, thus the
line "picture1.jpg c:\album02-12 christmas at home" has five elements with
the keys "picture1.jpg", "christmas" and "home".

What you really need is:

chomp( my %hash = map { split(/ /, $_, 2) } <LISTING> );

This splits the line into two parts and we get

picture2.jpg => c:\album02-07 july 4th in DC
picture1.jpg => c:\album02-12 christmas at home

However the

rename $key, $value . $key;

also has a problem. What you really want it

rename $key, "$value$key";

Assuming that $value is the name of a directory and $key the file name. You code
would try and create a file called:

"c:\album02-07 july 4th in DCpicture2.jpg".

Otherwise your code looks good and you have clearly got a good grasp of the perl
way. Keep it up.

Site Timeline