0

I'm attempting to write a script that will take in a directory path to a folder, and rename all the files in that folder to a name plus a number. For instance, rename img_0936.JPG, img_0937.JPG to hockeyPic_001.JPG and hockeyPic_002.JPG.

So here's my script:

folder = '/Users/ShaneWilliamson/Desktop/cobalt pics'
newName = 'cobaltPic'

puts 'Renaming files...'
files = Dir.entries(folder).reject{|f| f[0] == '.'}
puts files

i = 1
files.each do |file|
  File.rename(file, newName + i.to_s + '.JPG')
  i += 1
end

It's currently returning the correct file names (before the rename) but throwing:

Renaming files...
.
..
.DS_Store
image1.JPG
image2.JPG
image3.JPG
image4.JPG
image5.JPG
IMG_1586.JPG
IMG_1587.JPG
fileNamer.rb:10:in `rename': Invalid argument - . or cobaltPic_001.JPG
(Errno::EINVAL)
from fileNamer.rb:10
from fileNamer.rb:9:in `each'
from fileNamer.rb:9
Shanes-MacBook-Air:Desktop ShaneWilliamson$ 

So it seems the reject method isn't doing quite what I expect, which I think could be causing the error (system won't let the script change the 'hidden' files?).

In case it's relevant, I'm running OSX 10.7.5 on a MacBook Air. Ruby version 1.8.7 per 'ruby -v' command, which I think is just what came on the system out of the box.

  • Something I find useful for sorting by file name is to zero pad the numeric strings, and for easier reading, separating the stem and the number with an underscore. You could do something like this: `newName + ("_%03d" % i) + '.JPG'` => "newname_001.JPG" – Keith Bennett May 05 '16 at 02:28
  • Also, rather than explicitly initializing and incrementing `i`, you can do this: `files.each.with_index(1) { |file, index| puts "#{index}: #{file}" }` – Keith Bennett May 05 '16 at 02:30
  • Also, 1.8 is very ancient, and will be less and less supported by gems, and no longer supported with regard to security, fixes, etc. I highly recommend installing a more recent Ruby. I like `rvm` for this. Also, I recommend conforming to the Ruby snake case variable naming scheme, e.g. 'new_name' and not 'newName'. – Keith Bennett May 05 '16 at 02:33
  • @KeithBennet thanks for your responses. I really appreciate it. I'm with you on the renaming using the _001 convention, just wasn't sure how to do it, frankly. So thanks. Would you mind elaborating a little bit on your solution to avoid explicitly initializing and incrementing? Just trying to really understand this as all of this is a learning exercise as much as anything else. Specifically, I'm not sure I understand what you're doing from with_index forward. Thanks! – Shane Williamson May 05 '16 at 17:57
  • Shane, when you call with_index on `each`'s return value, it adds the index to the list of values that is passed to each iteration of the block. The parameter `1` just says start at number 1 and increment from there. – Keith Bennett May 05 '16 at 18:00

2 Answers2

1

A quick and relatively safe way is to rename only those files whose names do NOT start with a dot. Those ".xxx" files are "hidden files" that are created typically by the operating system or some softwares, and they are not intended to be touched.

folder = '/Users/ShaneWilliamson/Desktop/cobalt pics'
newName = 'cobaltPic'

Dir.entries(folder).reject{|f| f[0] == '.'}.each_with_index(1) do |file, i|
  File.rename file, "#{newName}#{i}.JPG"
end
Aetherus
  • 8,720
  • 1
  • 22
  • 36
  • @tadman both `.each_with_index` and `.each.with_index` are valid for an `Enumerable`. I often use `.each.with_index` because sometimes the receiver is not an `Enumerable`, only something that `.respond_to?(:each)`. – Aetherus May 04 '16 at 15:45
  • Ah, the [`with_index`](http://ruby-doc.org/core-2.3.0/Enumerator.html#method-i-with_index) method is an Enumerator method. They really should make both work the same way as being able to specify a starting index is nice. Thanks for pointing that out. That's a tidy way of solving this problem. – tadman May 04 '16 at 15:48
0

You might consider using an existing utility instead of writing your own. rename does just what you are trying to achieve and is available on most operating systems. Your whole script can be written as:

rename --just-print -N 001 's/.*/cobaltPic$N.jpg/' *.jpg

This command only shows you what the script would do, but doesn't actually rename anything. After you verify the output, you can remove the --just-print flag and it will apply the changes.

user12341234
  • 6,573
  • 6
  • 23
  • 48