names_to_numbers.rb: add --glob argument.
[cmccabe-bin] / names_to_numbers.rb
1 #!/usr/bin/ruby -w
2
3 #
4 # names_to_numbers.rb
5 #
6 # Renames all the files in a directory with the given file extension.
7 # The renamed files will just have numbers.
8 #
9 # Colin Mccabe
10 #
11
12 require 'fileutils'
13 require 'optparse'
14 require 'ostruct'
15
16 class MyOptions
17   def self.parse(args)
18     opts = OpenStruct.new
19     opts.dry_run = false
20     opts.num_digits = 2
21     opts.extension = nil
22     opts.starting_number = 1
23     $fu_args = { :verbose => true }
24     opts.preserve_names = false
25
26     # Fill in $opts values
27     parser = OptionParser.new do |myparser|
28       myparser.banner = "Usage: #{ File.basename($0) } [opts]"
29       myparser.separator("Specific options:")
30       myparser.on("--num-digits DIGITS", "-D",
31               "Set the number of digits in the numbering scheme.") do |d|
32         opts.num_digits = d.to_i
33       end
34       myparser.on("--dry-run", "-d",
35               "Show what would be done, without doing it.") do |d|
36         $fu_args = { :verbose => true, :noop => true }
37         opts.dry_run = true
38       end
39       myparser.on("--file-extension EXTENSION", "-e",
40               "The file extension for the files to rename.") do |e|
41         opts.extension = e
42       end
43       myparser.on("--preserve-names", "-p",
44               "Preserve the names while changing the numbers.") do |e|
45         opts.preserve_names = true
46       end
47       myparser.on("--starting-number NUMBER", "-N",
48               "The starting number (defaults to 1)") do |e|
49         opts.starting_number = e.to_i
50       end
51       myparser.on("--glob GLOB", "-g",
52               "Specify the glob expression to use.  Example: '*/*.mp3'.") do |d|
53         opts.glob = d
54       end
55     end
56
57     parser.parse!(args)
58     raise "invalid num_digits: #{opts.num_digits}" unless
59       opts.num_digits > 0
60     raise "must specify an extension" unless opts.extension != nil
61     if (opts.glob == nil) then
62       opts.glob = "*.#{$opts.extension}"
63     end
64     return opts
65   end
66 end
67
68 def pow(x, y)
69   ret = 1
70   (0...y).each do |z|
71     ret = ret * x
72   end
73   return ret
74 end
75 def file_iter
76   Dir.glob($opts.glob).sort.each do |f|
77     yield f
78   end
79 end
80
81 def count_files(file)
82   $total_files = $total_files + 1
83 end
84
85 def get_file_name(num)
86   return sprintf("%0#{$opts.num_digits}d", num)
87 end
88
89 def rename_files(file)
90   dst="#{get_file_name($opts.starting_number + $total_files)}.#{$opts.extension}"
91   if (file != dst) then
92     FileUtils.mv(file, dst, $fu_args)
93   end
94   $total_files = $total_files + 1
95 end
96
97 def rename_files_keep_names(file)
98   proper_file = ""
99   if (file =~ /^[0-9. -]*(.*)$/) then
100     proper_file = $1
101   else
102     raise "can't find proper name for #{file}"
103   end
104   full_name = "#{get_file_name(1 + $total_files)} - #{proper_file}"
105   FileUtils.mv(file, full_name, $fu_args)
106   $total_files = $total_files + 1
107 end
108
109 # MAIN
110 begin
111   $opts = MyOptions.parse(ARGV)
112 rescue Exception => msg
113   $stderr.print("#{msg}.\nType --help to see usage information.\n")
114   exit 1
115 end
116
117 # make sure there aren't too many files
118 $total_files = 0
119 max_total_files = pow(10, $opts.num_digits) - 1
120 file_iter { |f| count_files(f) }
121 if ($total_files > max_total_files) then
122   raise "With #{$opts.num_digits} digit(s), we can only have at most \
123 #{max_total_files} files-- but there are #{$total_files} files in the \
124 #directory. Try setting a higher value for num_digits, using -D."
125 end
126
127 # rename files
128 $total_files = 0
129 if ( $opts.preserve_names ) then
130   file_iter { |f| rename_files_keep_names(f) }
131 else
132   file_iter { |f| rename_files(f) }
133 end
134
135 exit 0