Antony
Antony

Reputation: 5454

How to mass rename files in ruby

I have been trying to work out a file rename program based on ruby, as a programming exercise for myself (I am aware of rename under linux, but I want to learn Ruby, and rename is not available in Mac).

From the code below, the issue is that the .include? method always returns false even though I see the filename contains such search pattern. If I comment out the include? check, gsub() does not seem to generate a new file name at all (i.e. file name remains the same). So can someone please take a look at see what I did wrong? Thanks a bunch in advance!

Here is the expected behavior: Assuming that in current folder there are three files: a1.jpg, a2.jpg, and a3.jpg The Ruby script should be able to rename it to b1.jpg, b2.jpg, b3.jpg

#!/Users/Antony/.rvm/rubies/ruby-1.9.3-p194/bin/ruby

puts "Enter the file search query"
searchPattern = gets
puts "Enter the target to replace"
target = gets
puts "Enter the new target name"
newTarget = gets
Dir.glob("./*").sort.each do |entry|
  origin = File.basename(entry, File.extname(entry))
  if origin.include?(searchPattern)
    newEntry = origin.gsub(target, newTarget)
    File.rename( origin, newEntry )
    puts "Rename from " + origin + " to " + newEntry
  end
end

Upvotes: 12

Views: 11694

Answers (6)

Gabriel Guérin
Gabriel Guérin

Reputation: 468

In a folder, I wanted to remove the trailing underscore _ of any audio filename while keeping everything else. Sharing my code here as it might help someone. What the program does:

  1. Prompts the user for the:
    • Directory path: c:/your/path/here (make sure to use slashes /, not backslashes, \, and without the final one).
    • File extension: mp3 (without the dot .)
    • Trailing characters to remove: _
  2. Looks for any file ending with c:/your/path/here/filename_.mp3 and renames it c:/your/path/here/filename.mp3 while keeping the file’s original extension.

puts 'Enter directory path'

path = gets.strip

directory_path = Dir.glob("#{path}/*")

# Get file extension

puts 'Enter file extension'

file_extension = gets.strip

# Get trailing characters to remove

puts 'Enter trailing characters to remove'

trailing_characters = gets.strip

suffix = "#{trailing_characters}.#{file_extension}"

# Rename file if condition is met

directory_path.each do |file_path|
  next unless file_path.end_with?(suffix)

  File.rename(file_path, "#{file_path.delete_suffix(suffix)}.#{file_extension}")
end

Upvotes: 0

Cid Santos
Cid Santos

Reputation: 21

I made a small script to rename the entire DBZ serie by seasons and implement this:

count = 1
new_name = "Dragon Ball Z S05E"
format_file = ".mkv"
Dir.glob("dragon ball Z*").each do |old_name|
  File.rename(old_name, new_name + count.to_s + format_file)
  count += 1
end

The result would be: Dragon Ball Z S05E1 Dragon Ball Z S05E2 Dragon Ball Z S05E3

Upvotes: 1

SimonS
SimonS

Reputation: 926

Here's a short version I've used today (without pattern matching)

Save this as rename.rb file and run it inside the command prompt with ruby rename.rb

count = 1
newname = "car"
Dir["/path/to/folder/*"].each do |old|
  File.rename(old, newname + count.to_s)
  count += 1
end

I had /Copy of _MG_2435.JPG converted into car1, car2, ...

Upvotes: 2

Josh Hunter
Josh Hunter

Reputation: 1687

I used the accepted answer to fix a bunch of copied files' names.

Dir.glob('./*').sort.each do |entry|
  if File.basename(entry).include?(' copy')
    newEntry = entry.gsub(' copy', '')
    File.rename( entry, newEntry )
  end
end

Upvotes: 4

bta
bta

Reputation: 45057

Slightly modified version:

puts "Enter the file search query"
searchPattern = gets.strip
puts "Enter the target to replace"
target = gets.strip
puts "Enter the new target name"
newTarget = gets.strip
Dir.glob(searchPattern).sort.each do |entry|
  if File.basename(entry, File.extname(entry)).include?(target)
    newEntry = entry.gsub(target, newTarget)
    File.rename( entry, newEntry )
    puts "Rename from " + entry + " to " + newEntry
  end
end

Key differences:

  • Use .strip to remove the trailing newline that you get from gets. Otherwise, this newline character will mess up all of your match attempts.
  • Use the user-provided search pattern in the glob call instead of globbing for everything and then manually filtering it later.
  • Use entry (that is, the complete filename) in the calls to gsub and rename instead of origin. origin is really only useful for the .include? test. Since it's a fragment of a filename, it can't be used with rename. I removed the origin variable entirely to avoid the temptation to misuse it.

For your example folder structure, entering *.jpg, a, and b for the three input prompts (respectively) should rename the files as you are expecting.

Upvotes: 12

Phrogz
Phrogz

Reputation: 303244

Your problem is that gets returns a newline at the end of the string. So, if you type "foo" then searchPattern becomes "foo\n". The simplest fix is:

searchPattern = gets.chomp

I might rewrite your code slightly:

$stdout.sync
print "Enter the file search query: "; search  = gets.chomp
print "Enter the target to replace: "; target  = gets.chomp
print "  Enter the new target name: "; replace = gets.chomp
Dir['*'].each do |file|
  # Skip directories
  next unless File.file?(file)
  old_name = File.basename(file,'.*')
  if old_name.include?(search)
    # Are you sure you want gsub here, and not sub?
    # Don't use `old_name` here, it doesn't have the extension
    new_name = File.basename(file).gsub(target,replace)
    File.rename( file, new_path )
    puts "Renamed #{file} to #{new_name}" if $DEBUG
  end
end

Upvotes: 3

Related Questions