More Elisp posts
October 27, 2002
Bulk file renames

I wanted to rename all of the files in a directory that ended in .txt so that they ended in .xml . Clearly some sort of script was needed, but what language to use? I chose elisp, because I was working in emacs and the interactive help made it easy to find the functions I wanted. Thinking about it later I wondered how I'd do this in other languages so here also are python and perl versions with the same functionality. None are heavily tested but all work if run in the directory containing the files to rename. My final impression? The elisp one was the quickest to write and I think is the easiest to manage for this job.

In elisp:

(defun rename-some-files (dir)
  "Rename some files."
  (interactive "DDirectory name ")
  (mapc (lambda (f) (rename-file (concat f ".txt") 
				 (concat f ".xml")))
	(mapcar 'file-name-sans-extension
		(directory-files dir nil "\.txt$")))

In python:

import os
import re

pat = re.compile('\.txt$')
map(lambda f: os.rename(f + '.txt', f + '.xml'),
    [os.path.splitext(f)[0] for f in os.listdir(os.getcwd()) \
     if pat.search(f)])

In perl:

use Cwd;
use File::Basename;

opendir(DIR, cwd());
map(rename($_ . '.txt', $_ . '.xml'),
    map(basename($_, '.txt'),
	grep(/\.txt$/, readdir(DIR))));
closedir(DIR);
Posted by Alex at October 27, 2002 06:46 PM
Comments
Python's os.path.walk() is a nice little util, if you wanted a recursive version of you code. I believe it also does some sort of directory caching, but it's been a while since I've used Python. I believe it would have been more maintainable Python to split the map into two lines. Because Python is not a functional language, and the indentation thre doesn't really help you. Posted by: Tj on October 28, 2002 03:57 AM
BTW, how do you insert newlines into these comments? Hitting enter doesn't do it, and html is stripped. Posted by: Tj on October 28, 2002 03:59 AM
os.path.walk() isn't nice - it's horrid! See http://groups.google.com/groups?selm=RoLM6.10846%24sk3.2912386%40newsb.telia.net Posted by: Simon on October 28, 2002 07:35 AM
Well, the thing is it's great if you like functional programming styles. So I'm glad that Guido made the "mistake" of including filter/map/reduce/lambda, because I wouldn't have used Python otherwise, and I've learned how to write it so it's clear to anyone. In fact, 70% of my code is some filter or map. But admittedly, os.path.walk's syntax is hard to remember; you just have to cut & paste it. Not hard, I use it all the time, just annoying. Posted by: Tj on October 28, 2002 09:23 AM
Use the shell. If you're on windows, get Cygwin. Then you can just do for i in *.txt; do mv $i ${i%%.txt}.xml; done from the command line. Posted by: JLM on October 29, 2002 09:32 AM
The shell solution's nice. I'm not familiar enough with shell coding to be able to knock this sort of stuff out quickly though. The biggest inhibitor for quick and dirty scripts is "how quickly can I write this stuff or find something prewritten". With elisp and the build in help (C-h f function-name) it's quick to find the things you need. You also need, especially with perl, to have a good idea about what all of the functions etc are before you can find them in the manual. Posted by: Alex on October 29, 2002 01:28 PM
Perl's core File::Find module can make this a lot easier. use Cwd; use File::Find; find(sub { /^(.*)(?:\.txt)$/ and rename $_, "$1.xml" }, cwd()) Posted by: Zed on November 29, 2004 03:06 PM
Perl's core File::Find module can make this a lot easier. use Cwd; use File::Find; find(sub { /^(.*)(?:\.txt)$/ and rename $_, "$1.xml" }, cwd()) Posted by: Zed on November 29, 2004 03:07 PM
Actually, I'd misread your intent as doing this recursively through subdirectories under the current directory. For a single directory, File::Find is overkill. perl -e '/^(.*)(?:\.txt)$/ and rename $_, "$1.xml" for ' Posted by: Zed on November 29, 2004 03:41 PM
Post a comment
Name:


Email Address:


URL:


Comments:


Remember info?