<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>jlh's assorted stuff</title>
	<atom:link href="http://atdot.ch/jas/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://atdot.ch/jas</link>
	<description>Pretty random</description>
	<lastBuildDate>Fri, 02 Jan 2009 12:33:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Directory renamer script (dirrename)</title>
		<link>http://atdot.ch/jas/?p=9</link>
		<comments>http://atdot.ch/jas/?p=9#comments</comments>
		<pubDate>Sat, 18 Oct 2008 13:11:55 +0000</pubDate>
		<dc:creator>jlh</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[rename]]></category>
		<category><![CDATA[utility]]></category>

		<guid isPermaLink="false">http://atdot.ch/jas/?p=9</guid>
		<description><![CDATA[Did you ever have to rename many files at once, using a certain pattern? For example rename &#8220;DSCXXX.JPG&#8221; to &#8220;Picture XXX.jpg&#8221;, where XXX goes from 001 to 123? Of course you could write a shell one-liner to do the job, but it&#8217;s much simpler and quicker if you can load the list of file names [...]]]></description>
			<content:encoded><![CDATA[<p>Did you ever have to rename many files at once, using a certain pattern?  For example rename &#8220;DSCXXX.JPG&#8221; to &#8220;Picture XXX.jpg&#8221;, where XXX goes from 001 to 123?  Of course you could write a shell one-liner to do the job, but it&#8217;s much simpler and quicker if you can load the list of file names into your favourite text editor and then use standard editor features (search &amp; replace) to rename the files.  This is exactly what this script does: You call it without arguments and it loads all file names of the current directory into $EDITOR, let&#8217;s you edit them, and when you&#8217;re done, it renames all files accordingly.  It&#8217;s simple, yet powerful, even though it lacks many bells and whistles yet.  It correctly handles cycles, so you can exchange two file names if you like.</p>
<p>For convenience, copy the following into /usr/local/bin/dirrename and make it executable:</p>
<pre>#!/usr/bin/perl -w

# Copyright (c) 2008 jlh (jlh at gmx dot ch)
#
# This bit of code is released under the GPL version 2 or (at your option) any
# later version.

use strict;
use warnings;

my $get_tmp_name_n = 0;

sub get_tmp_name {
	my $tmp;

	while (1) {
		$get_tmp_name_n++;
		$tmp = ".__dirrename_tmp_${get_tmp_name_n}__";
		last if !-e $tmp;
	}

	return $tmp;
}

# check if we have write access to .

if (!-w '.') {
	print "I do not have write access to this directory.\n";
	exit 1;
}

# read the list of files to rename

opendir my $h, '.' or die;
my @list = sort grep { !/^\./ } readdir $h;
closedir $h;

# write it to a file

my $tmp = "/tmp/pid.$$";
open $h, '&gt;', $tmp or die;
print $h join("\n", @list), "\n";
close $h;

# let the user edit it

my $editor = $ENV{EDITOR} || 'vi';
system("$editor '$tmp'");

# read it back in

open $h, '&lt;', $tmp or die;
my @newlist = map { my $a = $_; chomp $a; $a; } &lt;$h&gt;;
close $h;
unlink $tmp;

# compare

if (@list != @newlist) {
	print "Number of lines has been changed!";
	exit 1;
}

# check for errors

my $error = 0;

my %from;
$from{$_}++ for @list;
my %to;
$to{$_}++ for @newlist;

for (keys %to) {
	if ($to{$_} &gt; 1) {
		print "error: more than one file wants to rename to '$_'\n";
		$error = 1;
	}
	if (!exists $from{$_} and -e $_) {
		print "error: target exists already: $_\n";
		$error = 1;
	}
}

exit $error if $error;

# build a trivial strategy to rename the file (rename all files to a temporary
# name, then all temporary names are renamed to the target names; this avoids
# cycles and dependencies)

my @strategy;

for (0 .. $#list) {
	my ($old, $new) = ($list[$_], $newlist[$_]);
	next if $old eq $new;
	my $tmp = &amp;get_tmp_name;
	unshift @strategy, [ $old, $tmp ];
	push @strategy, [ $tmp, $new ];
}

# now optimize this strategy.  this avoids unnecessary renaming but takes more
# time to plan.  it would probably be faster without doing this, but it was fun
# to write it.  :)

while (1) {

	my $done_something = 0;

	for (my $i = 0; $i &lt; @strategy; $i++) { # regular loop because we splice @strategy in here
		my $target = $strategy[$i][1];
		my $j = undef;
		for ($i + 1 .. $#strategy) {
			if ($strategy[$_][1] eq $target) {
				die "Assertion failure...\n";
			}
			if ($strategy[$_][0] eq $target) {
				$j = $_;
				last;
			}
		}
		if (defined $j) {
			my $target_target = $strategy[$j][1];

			# see if we can merge $strategy[$i] and $strategy[$j] into $strategy[$i]...

			my $ok = 1;
			for ($i + 1 .. $j - 1) {
				if ($strategy[$_][0] eq $target_target or $strategy[$_][1] eq $target_target) {
					$ok = 0;
					last;
				}
			}

			if ($ok) {
				# ...yes we can, do it
				#print "reducing chain: $strategy[$i][0] -&gt; $target -&gt; $target_target (up)\n";
				$strategy[$i][1] = $target_target;
				splice @strategy, $j, 1;
				next;
			} 

			# we couldn't, try to merge $strategy[$i] and $strategy[$j] into $strategy[$j] instead

			my $from = $strategy[$i][0];
			$ok = 1;
			for ($i + 1 .. $j - 1) {
				if ($strategy[$_][0] eq $from or $strategy[$_][1] eq $from) {
					$ok = 0;
					last;
				}
			}

			if ($ok) {
				# ...yes we can, do it
				#print "reducing chain: $strategy[$i][0] -&gt; $target -&gt; $target_target (down)\n";
				$strategy[$j][0] = $from;
				splice @strategy, $i, 1;
				$i--;
				next;
			}
		}
	}

	last unless $done_something;
}

# execute it

print "Performing " . scalar(@strategy) . " rename operations\n";

for (@strategy) {
	#print "$_-&gt;[0] -&gt; $_-&gt;[1]\n";
	rename $_-&gt;[0], $_-&gt;[1];
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://atdot.ch/jas/?feed=rss2&#038;p=9</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

