Renaming stuff with Ruby
I've been mucking around on the command line with Ruby again of late, and one thing that tripped me up initially, but is now firmly on my list of things I Like About Programming is how Ruby handles loops.
With javascript, actionscript, php, it's pretty common to iterate through an array with a for loop something like this:
for (i =o; i = array.length; i ++ ) {
// call function foo() on array iteration 'i', with argument 'bar'
array[i].foo(bar)
}
This is doable in Ruby too, but the following is much more common:
array do |i|
// call function 'foo' on array iteration 'i'
i.foo(bar)
end
You could argue that you lose some context inside the loop if it's a long enough method, but for quick loops like in this file renaming widget me and Chris Mear wrote for the Open Rights Group, the expressiveness is pretty impressive.
Here's the full code we used for renaming the 6000-odd files that Photoshop spat out for the ORG founding 1000 badges. Chris knocked this out in about 5 minutes, and I've now commented it to the point that it's parsable even when I'm hungover.
# this script iterates through about 6000 files that have been generated by Photoshop,
# then renames them to fit naming conventions for a web app, as specified in a CSV file 'codes.csv'
# The Ruby CSV module allows to iterate through csv file easily
require 'csv'
# declare array literal to put desired filenames into
codes = []
# take column two of each row in codes.csv, and put it into
# the first column of each row of the recently declared codes array
CSV.open("codes.csv", 'r') do |row|
codes[row[1].to_i] = row[0]
end
# switch into the directory 'export'
Dir.chdir 'export'
# bookmark current directory to return to later
wd = Dir.getwd
# glob is a function that returns an array of items matching the pattern given.
# in this case it this returns pretty much every directory or file name everything
# we could also substitute with Dir.entries('.')
('*') do |dirname|
# Dir.chdir is the Ruby equivalent to 'cd dirname'
Dir.chdir(dirname)
# perform a search for all .png files, (all files ending with '.png' )
Dir.glob('*.png') do |filename|
# read each filename, find the part of the filename that isn't the number (the '/\D/' part of the regular expression),
# then strip it out, leaving just the number, making sure to convert this to an integer, not a string
number = filename.gsub(/\D/, '').to_i
# local variable 'code' exists inside this loop only
code = codes[number]
new_filename = "#{code}.png"
puts "Renaming #{filename} to #{new_filename}."
File.rename(filename, new_filename)
end
# switch back to the starting directory before looping through the
# next folder
Dir.chdir(wd)
end