1
0
.dotfiles/home/bin/datename

376 lines
8.3 KiB
Perl
Executable File

#!/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] <files>
# 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>", "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>",
"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>", "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>", "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 = <STDIN>);
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 <<EOF;
NAME
datename - Massively rename multiple files
USAGE
datename [options] <files>
OPTIONS
The following options can be provided at the command line, or will be
prompted for during operation.
--format, -f <format>
Provide the date format. Should contain yyyy, mm, dd, and a
sequence of at least one n. Ex: yyyy-mm-dd_nnn
--date, -d <date>
Provide the date. Should be in yyyy-mm-dd format, e.g.
2009-02-23
--start, -s <number>
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 <directory>
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);
}