#!/usr/bin/perl -w # datename - Automatically rename a large group of files to have dates in their # names. # # This script is for renaming many files (e.g. files downloaded from a digital # camera) to have dates as their file names, e.g. from 2009-12-25_001.jpg to # 2009-12-25_058.jpg # # Usage: datename [options] # See `datename -?` for more help. # # --Kirsle # http://sh.kirsle.net/ use strict; use warnings; use Getopt::Long; use File::Copy; our $version = "1.0 Feb 23 2009"; unless (@ARGV) { print "Usage: datename [options] [files]\n" . "Try `datename -?` for help.\n"; exit(1); } our %c = ( r => "\e[31m", g => "\e[32m", c => "\e[34m", o => "\e[0m", ); ############################## # Collect Options # ############################## my @lt = localtime(time()); my $today = join("-", sprintf("%04d", ($lt[5] + 1900)), sprintf("%02d", ($lt[4] + 1)), sprintf("%02d", ($lt[3])), ); my $o = { help => 0, format => undef, date => undef, start => 1, force => 0, backup => "./datename-backup", nobackup => 0, mono => 0, }; GetOptions ( 'help|h|?' => \$o->{help}, 'format|f=s' => \$o->{format}, 'date|d=s' => \$o->{date}, 'start|s=i' => \$o->{start}, 'backup|b=s' => \$o->{backup}, 'nobackup' => \$o->{nobackup}, 'force' => \$o->{force}, 'monotone|mono|m' => \$o->{mono}, ); if ($o->{help}) { &help(); } if ($o->{mono}) { foreach my $key (keys %c) { $c{$key} = ''; } } ############################## # Ask for Parameters # ############################## $| = 1; print "$c{r}DateName$c{o} version $c{g}$version$c{o}\n\n"; unless ($o->{nobackup}) { print "Initializing backup directory... "; if (!-d $o->{backup}) { system("mkdir", "-p", $o->{backup}); if (!-d $o->{backup}) { die "Can't create backup directory: $!"; } } print "Done!\n\n"; } if (!defined $o->{format}) { print "$c{c}1: Enter the format for the file names to follow. This should\n" . " contain the sequences `yyyy`, `mm`, `dd`, and at least one\n" . " `n`. For example if the format is `yyyy-mm-dd_nnn`, the\n" . " and the date is 2009-02-23, the first file will be named\n" . " 2009-02-23_001.jpg, the second 2009-02-23_002.jpg, and\n" . " so-on. The default is yyyy-mm-dd_nnn. You can simply hit\n" . " return here if you want to keep the default.$c{o}\n\n"; while (1) { my $format = &prompt("Enter the date format, or blank for " . "", "yyyy-mm-dd_nnn"); # Validate the format. if ($format !~ /yyyy/ || $format !~ /mm/ || $format !~ /dd/ || $format !~ /n+/) { print "\n$c{r}You've entered an invalid date format. " . "Try again.$c{o}\n\n"; } else { # Good. $o->{format} = $format; last; } } } if (!defined $o->{date}) { print "\n$c{c}2: Enter the date that you want these files to be renamed\n" . " after. Enter the date in the format of yyyy-mm-dd.\n" . " Today's date is $c{g}$today$c{c}.$c{o}\n\n"; while (1) { my $date = &prompt("Enter the date to rename the files after, " . "or <$today>", $today); # Validate the date. if ($date !~ /^(\d\d\d\d)\-(\d\d)\-(\d\d)$/) { print "\n$c{r}You've entered an invalid date. The date\n" . "must be in yyyy-mm-dd format.$c{o}\n\n"; } else { # Good. $o->{date} = $date; last; } } } if (1) { print "\n$c{c}3: Your files will be renamed beginning with the number " . $c{g} . $o->{start} . "$c{c}.$c{o}\n\n"; my $answer = &prompt("Okay to begin at the number $o->{start}? " . " [y/n] ", "y", qw(y yes n no)); if ($answer =~ /^n/i) { while (1) { my $start = &prompt("What number do you want to " . "start at, or <1>", 1); if ($start !~ /^\d+$/) { print "\n$c{r}Invalid answer.$c{o}\n\n"; } else { $o->{start} = $start; last; } } } } ############################## # Summarize What's Going On # ############################## my @files = &getFileList(); my $numFiles = scalar(@files); my ($nss) = ($o->{format} =~ /(n+)/i); our $ns = length $nss; our ($year,$mon,$day) = ($o->{date} =~ /^(\d\d\d\d)\-(\d\d)\-(\d\d)$/); if (1) { my $backuptext = "Your files will be backed up to $o->{backup}."; if ($o->{nobackup}) { $backuptext = "Your files will *NOT* be backed up."; } my $first = &datename($o->{start}); my $last = &datename($o->{start} + $numFiles); print "\n" . $c{c} . ("=" x 70) . "$c{o}\n" . "$c{g}Summary of Operations:$c{o}\n\n" . "$c{c}Your $numFiles files are going to be renamed in the format\n" . "'$o->{format}' using the date '$o->{date}', beginning with\n" . "the number $o->{start}. They will be renamed from\n" . "$first to $last.\n\n" . "$backuptext$c{o}\n\n"; my $proceed = &prompt("Okay to proceed? [y/n] ", "n", qw(y yes n no)); unless ($proceed =~ /^y/i) { print "\nAborting procedure!\n"; exit(0); } } ############################## # Main Operation # ############################## my $int = $o->{start}; foreach my $file (@files) { next unless -f $file; my ($ext) = ($file =~ /\.([A-Za-z0-9]+?)$/i); $ext = "jpg" unless defined $ext; print "$c{c}Looking at file $file$c{o}\n"; unless ($o->{nobackup}) { my $backup = $file; my $bi = 1; while (-f "$o->{backup}/$backup") { $backup = "[$bi] $file"; $bi++; } print " Backing it up as $o->{backup}/$backup... "; copy ($file, "$o->{backup}/$backup"); if (-f "$o->{backup}/$backup") { print "Done!\n"; } else { die "$c{r}Error: couldn't back it up: $!$c{o}"; } } my $newName = &datename($int) . "." . lc($ext); $int++; print " Renaming file to $newName" . ($o->{force} ? " (forced)" : "") . "... "; if (-f $newName && !$o->{force}) { print "Warning: File already exists!$c{r}\n"; my $continue = &prompt( " The file $newName already exists. Overwrite? " . "[y/n] ", "n", qw(y yes n no)); if ($continue !~ /^y/i) { print " Skipping rename of $file!$c{o}\n"; next; } print " Renaming file to $newName (forced)... $c{o}"; } # Rename it. rename ($file, $newName); print "$c{g}Done!$c{o}\n"; } print "\n" . "$c{g}Procedure completed. Backups were saved to $o->{backup}.$c{o}\n"; exit(0); sub datename { my $i = shift; my $format = $o->{format}; $format =~ s/yyyy/$year/ig; $format =~ s/mm/$mon/ig; $format =~ s/dd/$day/ig; my $sprint = sprintf("%0${ns}d", $i); $format =~ s/n+/$sprint/ig; return $format; } sub getFileList { if (@ARGV) { return (@ARGV); } my @return = (); opendir (DIR, "."); foreach my $f (sort(grep(/^\./, readdir(DIR)))) { if (-f $f) { push (@return, $f); } } closedir (DIR); return (@return); } sub prompt { my $question = shift; my $default = shift; my @accept = (); my $asking = 1; while ($asking) { print "$question "; chomp (my $answer = ); if (defined $answer && length $answer) { if (@accept) { foreach my $a (@accept) { if ($answer eq $a) { return $a; } } print "Invalid answer.\n"; } else { return $answer; } } else { if (@accept) { print "INvalid answer.\n"; } else { return $default; } } } } sub help { print < OPTIONS The following options can be provided at the command line, or will be prompted for during operation. --format, -f Provide the date format. Should contain yyyy, mm, dd, and a sequence of at least one n. Ex: yyyy-mm-dd_nnn --date, -d Provide the date. Should be in yyyy-mm-dd format, e.g. 2009-02-23 --start, -s Enter the iteration number to begin renaming files at. By default it is 1. The following options will modify the default behavior of the program: --backup, -b Specify the directory you want the files backed up into. Default is ./datename-backup This folder will try to be created if it doesn't exist. --nobackup Do not back up files (I don't recommend this option). --force Force rename all files (do not prompt the user if the file already exists). EXAMPLES ; Specify all the prompt questions on the command line and rename ; JPG and PNG images only. datename -f "yyyy-mm-dd_nnn" -d "2009-02-23" -s 100 *.jpg *.png ; Rename JPG, BMP, and GIF, will be prompted for the other options datename *.jpg *.bmp *.gif ; Rename everything datename * AUTHOR Casey Kirsle http://www.kirsle.net/ EOF exit(1); }