Search code examples
rubyfilerenamebulk

SystemStackError w/ bulk file extension renaming in Ruby


Here is my basic script to rename a file tree of files starting at the given directory:

require 'fileutils'

module Renamer
  class Application
    def initialize
      @directory = Dir.pwd
    end

    def rename(dir, extension, change_to)
      Dir.glob("#{dir}").each do |f|
        next if %w(. ..).include? f

        if File.directory? f
          rename f, extension, change_to
        else
          puts "Renaming file: #{f}"
          FileUtils.mv f, "#{File.dirname(f)}/#{File.basename(f, '.*')}.#{change_to}" if File.extname f == ".#{extension}"
        end
      end
    end

    def start
      puts 'What file extension do you want to change?'
      extension = gets.chomp
      puts 'What do you want to change it to?'
      change_to = gets.chomp

      rename @directory, extension, change_to

      puts 'Renaming completed!'
    end
  end

  app = Application.new
  app.start
end

For some reason it throws a SystemStackError at line 11, which is bizarre to me. If at the Dir.glob method I simply place a concatenation for the extension alongside the dir variable and delete the "if" modifier on the statement that actually renames the files, all of the files in the current directory are renamed but not anywhere else since I'm not digging into the other directories, and also tells me that the code is functional. How can this error be resolved?


Solution

  • A simple p f at the start of the block reveals the problem. Dir.glob expands file globs, but you're only passing Dir.pwd (with no glob). So your cwd is passed to rename, it is certainly a directory so it immediately passes itself to rename again, cue endless recursion and a stack error.

    If you change it to Dir.glob("#{dir}" + "/*") then you're actually globbing something!

    After doing so you'll also find that you need to add more parentheses on line 18 because the parser is getting confused.


    On a different note, I recommend the find library. It is specifically designed for recursively iterating through a directory.