Steve Steve - 4 months ago 15
Perl Question

file organisation in windows using perl

I am working on a windows machine and I have a directory filled with ~200k of files which I need to organise. This is a job I will need to do regularly with different filename sets but with similar patterns so perl seemed a good tool to use.

Each filename is made up of {a string A}{2 or 3 digit number B}{single letter "r" or "x"}{3 digit number}.extension

I want to create a folder for each string A

Within each folder I want a sub-folder for each B

I then want to move each file into its relevant sub-folder

So it will end up looking something like

/CustomerA/1
/CustomerA/2
/CustomerA/3
/CustomerB/1
/CustomerB/2
/CustomerB/3


etc with the files in each sub-folder

so CustomerA888x123.xml is moved into /CustomerA/888/

I have the list of files in an array but I am struggling with splitting the file name out to its constituent parts and using the parts effectively.

Thanks for the answer. I ended up with this:

#!usr/bin/perl
use warnings;
use strict;
use File::Copy qw(move);
use File::Path qw(make_path);

opendir my $dir, ".";
my @files = readdir($dir);
closedir $dir;

foreach my $file (@files) {
my ($cust, $num) = $file =~ m/(\D+)(\d+)/;
my $dirname = "$cust/$num";
my @dirs_made = make_path($dirname, { verbose => 1 });
move($file, $dirname) or warn "cant move $file to $dirname: $!";
}

Answer

Given your description of file names, this regex should parse what you need

my ($cust, $num) = $filename =~ m/(\D+)(\d+)/;

Use a more precise pattern if you wish or need to be more specific about what precedes the number, for example [a-zA-Z] for letters only.

With that on hand, you can create directories using the core module File::Path, for example

use File::Path qw(make_path);
my $dirname = "$cust/$num";
my @dirs_made = make_path($dirname, { verbose => 1 });

This creates the path as needed, returning the names of created directories. It also prints the names with the verbose. If the directory exists it quietly skips it. If there are problems it raises a die so you may want to wrap it in eval

eval { make_path($dirname) };
if ($@) { 
    warn "Error with make_path($dirname): $@";
}

Also note the File::Path::Tiny module as an alternative, thanks to Sinan Ünür for bringing it up. Other than being far lighter, it also has the more common error-handling policy whereby a false is returned on failure so you don't need an eval but only the usual check

use File::Path::Tiny;
File::Path::Tiny::mk($path) or warn "Can't mk($path): $!";

The module behaves similarly to mkdir in many ways, see the linked documentation.

Move the files using the move function form the core module File::Copy, for example

use File::Copy qw(move);
move($file, $dirname)  or warn "Can't move $file to $dirname: $!";

All this can be in a loop over the array with the file names.

Comments