Google


   


You are here: CodeIdol.com > Other > Ruby Cookbook > System Administration > Renaming Files In Bulk

SAVE
Digg
Shown on del.icio.us del.icio.us
See Whos Talking About This on Technorati Technorati
I've Reddit reddit

Recipe 23.6. 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. Here's a method that renames files according to the results of a code block. It returns a list of files it couldn't 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. I'll create some dummy files to demonstrate:

	require 'fileutils'
	tmp_dir = 'tmp_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 let's 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't be renamed, because oldfile.txt and subdir/i_am_shouting were already taken.

Let's 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 there's 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 you'll handle in some other way the files it couldn't 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. Here's 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, we'll create some dummy image files to test:

	tmp_dir = 'tmp_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, let's 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"


SAVE
Digg
Shown on del.icio.us del.icio.us
See Whos Talking About This on Technorati Technorati
I've Reddit reddit

You are here: CodeIdol.com > Other > Ruby Cookbook > System Administration > Renaming Files In Bulk


ADBRITE ads links
   
Related tags







Popular Categories
Unix books and guides

AJAX popular information
C# language guides
Windows books and cookbooks

.......








Business Key Top Sites

be number one
rate your site




    С 2009 года мы стали переводить структура сайта на различные языки. Сайт теперь будет содержать книги не только на английском языке, но также и на других европейских языках, в том числе и на Русском языке.

    Русский Polski Francais Deutsch
    support sitemap terms

© CodeIdol Labs, 2007 - 2009