Renaming Files in Bulk

Problem

You want to rename a bunch of files programmatically: for instance, to normalize the filename case or to change the extensions.

Solution

Use the Find module in the Ruby standard library. Heres a method that renames files according to the results of a code block. It returns a list of files it couldn rename, because their proposed new name already existed:

	require find
	
	module Find
	 def rename(*paths)
	 unrenamable = []
	 find(*paths) do |file|
	 next unless File.file? file # Skip directories, etc.
	 path, name = File.split(file)
	 new_name = yield name

	 if new_name and new_name != name
	 new_path = File.join(path, new_name)
	 if File.exists? new_path
	 unrenamable << file
	 else
	 puts " 
Renaming #{file} to #{new_path}" if $DEBUG
	 File.rename(file, new_path)
	 end
	 end
	 end
	 return unrenamable
	 end
	 module_function(:rename)
	end

This addition to the Find module makes it easy to do things like convert all filenames to lowercase. Ill create some dummy files to demonstrate:

	require fileutils
	tmp_dir = 	mp_files
	Dir.mkdir(tmp_dir)
	[CamelCase.rb, OLDFILE.TXT, OldFile.txt].each do |f|
	 FileUtils.touch(File.join(tmp_dir, f))
	end

	tmp_dir = File.join(tmp_dir, subdir)
	Dir.mkdir(tmp_dir)
	[i_am_SHOUTING, I_AM_SHOUTING].each do |f|
	 FileUtils.touch(File.join(tmp_dir, f))
	end

Now lets convert these filenames to lowercase:

	$DEBUG = true
	Find.rename(./) { |file| file.downcase }
	# 
Renaming ./tmp_files/subdir/I_AM_SHOUTING to ./tmp_files/subdir/i_am_shouting
	# Renaming ./tmp_files/OldFile.txt to ./tmp_files/oldfile.txt
	# Renaming ./tmp_files/CamelCase.rb to ./tmp_files/camelcase.rb
	# => ["./OldFile.txt", "./dir/i_am_SHOUTING"]

Two of the files couldn be renamed, because oldfile.txt and subdir/i_am_shouting were already taken.

Lets add a ".txt" extension to all files that have no extension:

	Find.rename(./) { |file| file + .txt unless file.index(.) }
	# 
Renaming ./tmp_files/subdir/i_am_shouting to ./tmp_files/subdir/i_am_shouting.txt
	# 
Renaming ./tmp_files/subdir/i_am_SHOUTING to ./tmp_files/subdir/i_am_SHOUTING.txt #
	# => []

Discussion

Renaming files in bulk is a very common operation, but theres no standard command-line application to do it because renaming operations are best described algorithmically.

The Find.rename method makes several simplifying assumptions. It assumes that you want to rename regular files and not directories. It assumes that you can decide on a new name for a file based solely on its filename, not on its full path. It assumes that youll handle in some other way the files it couldn rename.

Another implementation might make different assumptions: it might yield both path and name, and use autoversioning to guarantee that it can rename every file, although not necessary to the exact filename returned by the code block. It all depends on your needs.

Perhaps the most common renaming operation is modifying the extensions of files. Heres a method that uses Find.rename to make this kind of operation easier:

	module Find
	 def change_extensions(extension_mappings, *paths)
	 rename(*paths) do |file|
	 base, extension = file.split(/(.*)./)[1..2]
	 new_extension = extension
	 extension_mappings.each do |re, ext|
	 if re.match(extension)
	 new_extension = ext
	 break
	 end
	 end
	 "#{base}.#{new_extension}"
	 end
	 end
	 module_function(:change_extensions)
	end

This code uses Find.change_extensions to normalize a collection of images. All JPEG files will be given the extension ".jpg", all PNG files the extension ".png", and all GIF files the extension ".gif".

Again, well create some dummy image files to test:

	tmp_dir = 	mp_graphics
	Dir.mkdir(tmp_dir)

	[my.house.jpeg, Construction.Gif, DSC1001.JPG, 52.PNG].each do |f|
	 FileUtils.touch(File.join(tmp_dir, f))
	end

Now, lets rename:

	Find.change_extensions({/jpe?g/i => jpg,
	 /png/i => png,
	 /gif/i => gif}, tmp_dir)
	# 
Renaming tmp_graphics/52.PNG to tmp_graphics/52.png
	# 
Renaming tmp_graphics/DSC1001.JPG to tmp_graphics/DSC1001.jpg
	# Renaming tmp_graphics/Construction.Gif to tmp_graphics/Construction.gif
	# Renaming tmp_graphics/my.house.jpeg to tmp_graphics/my.house.jpg

See Also

  • Some Unix installations come with a program or Perl script called rename, which can do your renaming if you can represent it as a string substitution or a regular expression; you may not need anything else
  • Recipe 6.14, "Backing Up to Versioned Filenames"
  • Recipe 6.20, "Finding the Files You Want"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net