This perl script renames all files in a directory to have an equal amount of digits I took a lot of pictures with my camera and I wanted to make a time-lapse out of them. The camera saved the pictures as picture1, picture2 ... picture956 etc. but the time-lapse software I'm using only accepts numbers of equal length like this: picture001, picture002, picture003 etc.

I thought Perl would be a good fit for this kind of problem so I gave it a shot. This is a shorter and translated version of the original code so if anything is unclear I can change it to the longer version.

use strict;
use warnings;
use diagnostics;

print "Give the path to the directory where the pictures are stored:\n";
my $filename = &lt;STDIN&gt;;

chomp $filename;
chdir $filename or die "Couldn't change the directory: $!\n";
my @files  = &lt;*&gt;;
#gives a list of all filehandles in the given directory

my $amount_of_digits = int( log($#files)/log(10) +1);
#how many digits should the new number have?
#Example: If there are 400 files -&gt; 3 digits per file: 001, 002 etc

foreach my $file (@files){
    next if($file =~ /^\.$/);
    next if($file =~ /^\.\.$/);
    #skip the . and .. files
    print "$file\n";
    if ($file =~ /(\d+)/){
        my $amount_of_padded_zeroes = $amount_of_digits - length($1);
        if($amount_of_padded_zeroes &gt; 0){
            my $new_number =  '0' x $amount_of_padded_zeroes . $1;     
            (my $new_name = $file) =~ s/$1/$new_number/;
            print "Changing name too: $new_name\n"; 
            rename ($file, $new_name) or die "Couldn't rename the file: $!";
        }
    }
}

print "Program completed. Press any key to continue.\n";
my $einde = &lt;STDIN&gt;;



The code works but I wanted to know if there is a better/cleaner/shorter/more Pearlesque way to do this. I'm learning Perl for uni so any feedback is welcome. I suspect this problem is so trivial in Perl that there might be readable one-liners that are able to replace all of this.
As you suspect, this can be done in fewer lines, but it's not a one-liner.  


use strict;
use warnings;
use diagnostics;



Always a good idea.  Consider use warnings FATAL =&gt; "all" so that you don't miss any.




my $filename = &lt;STDIN&gt;;



A command-line argument or environment variable is the typical way to do this, in Perl and most other languages.  Directory variables oughtn't be named $filename.

  my $dir = ( shift or $ENV{TIMELAPSE_DIRECTORY} or die "usage: $0 directory\n" );





chdir $filename or die "Couldn't change the directory: $!\n";
…
rename ($file, $new_name) or die "Couldn't rename the file: $!";



The fat-arrow =&gt; can replace a comma and improve readability in some cases.  Dropping parens is another good readability boost, when done judiciously.  Unless the line number where die occurred is interesting, suppress it by appending a newline to the message.  And finally, it's a good habit to include in error messages the data that produced them, as in:

 rename $file =&gt; $new_name or die "Couldn't rename '$file' to '$new_name': $!\n";





my @files  = &lt;*&gt;;
#gives a list of all filehandles in the given directory



Filenames, not filehandles.  It would be reasonable to filter this list to contain actual files, excluding directories:

my @files  = grep -f, &lt;*&gt;;


And maybe even by name: 

my @files = grep { -f and /\.( png | jpe?g | tga )$/xi } &lt;*&gt;;





my $amount_of_digits = int( log($#files)/log(10) +1);



$#files is the largest index in that zero-based array.  With 10 files, the last is $files[9] and the math returns 1 instead of the 2 we need.  The size of the array is one bigger and retrieved as @files in scalar context (log() imposes scalar context for us, which is convenient).  

width is a good name for this variable.

Avoid an uncaught exception by checking that @files is non-empty.

And the parens around int can be omitted.  

die "nothing to do!\n" unless @files;
my $width = int log(@files)/log(10) + 1;


But this is still no good!  log(1000)/log(10) is 3 in Perl and in real life.  But int( log(1000)/log(10) ) is 2!   This happens because floating-point math is imperfect.  Luckily Perl will let us cheat by taking the length of a number that's been silently converted  to a string:

my $width = length scalar @files;





foreach my $file (@files){



for is the idiomatic alternative to foreach.




    if ($file =~ /(\d+)/){
        my $amount_of_padded_zeroes = $amount_of_digits - length($1);
        if($amount_of_padded_zeroes &gt; 0){
            my $new_number =  '0' x $amount_of_padded_zeroes . $1;     
            (my $new_name = $file) =~ s/$1/$new_number/;
            print "Changing name too: $new_name\n"; 



sprintf is the function to use for leading zeroes.  It can go right in the s/// replacement by using the /execute modifier.  If the replace fails, skip to next file.

             (my $new_name = $file) =~ s/(\d+)/ sprintf "%0${width}d" =&gt; $1 /e or next;





rename ($file, $new_name) or die "Couldn't rename the file: $!";



It's good practice to ensure you have something to do, and that you aren't overwriting an existing file here:

next if $new_name eq $file;
die "$new_name (from $file) already exists!\n" if -f $new_name;





print "Program completed. Press any key to continue.\n";
my $einde = &lt;STDIN&gt;;



I think you know how I feel about this.  Should you decide to keep it, only the Enter key will actually proceed.

Putting it all together:

use strict;
use warnings FATAL =&gt; 'all';
use diagnostics;

my $dir = ( shift or $ENV{TIMELAPSE_DIRECTORY} or die "usage: $0 directory\n" );
chdir $dir or die "Couldn't change directory to $dir: $!\n";
my @files = grep { -f and /\.( png | jpe?g | tga )$/xi } &lt;*&gt;;
die "nothing to do!\n" unless @files;
my $width = length scalar @files;

for my  $file (@files) { 
    (my $new_name = $file) =~ s/(\d+)/ sprintf "%0${width}d" =&gt; $1 /e or next;
    next if $new_name eq $file;
    die "$new_name (from $file) already exists!\n" if -f $new_name;
    rename $file =&gt; $new_name or die "Couldn't rename '$file' to '$new_name': $!\n";
}
print "Program completed.\n"


Reader exercise: improve this program to number the files from 1 to n, even if the original numbers don't start at 1 or have gaps!