Class: File::Find

Inherits:
Object
  • Object
show all
Defined in:
lib/file/find.rb

Constant Summary collapse

VERSION =

The version of the file-find library

'0.3.7'
VALID_OPTIONS =

:stopdoc:

%w/
  atime
  ctime
  follow
  ftype
  inum
  group
  links
  maxdepth
  mindepth
  mount
  mtime
  name
  pattern
  path
  perm
  prune
  size
  user
/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Find

Creates and returns a new File::Find object. The options set for this object serve as the rules for determining what files the File::Find#find method will search for.

In addition to the standard list of valid options, you may also use FileTest methods as options, setting their value to true or false.

Example:

rule = File::Find.new(
   :name      => "*.rb",
   :follow    => false,
   :path      => ['/usr/local/lib', '/opt/local/lib'],
   :readable? => true
)


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/file/find.rb', line 170

def initialize(options = {})
  @options = options

  @atime  = nil
  @ctime  = nil
  @ftype  = nil
  @group  = nil
  @follow = true
  @inum   = nil
  @links  = nil
  @mount  = nil
  @mtime  = nil
  @perm   = nil
  @prune  = nil
  @size   = nil
  @user   = nil

  @previous = nil
  @maxdepth = nil
  @mindepth = nil
  @filetest = []

  validate_and_set_options(options) unless options.empty?

  @filesystem = File.stat(@mount).dev if @mount

  @path ||= Dir.pwd
  @name ||= '*'
end

Instance Attribute Details

#atimeObject

Limits searches by file access time, where the value you supply is the number of days back from the time that the File::Find#find method was called.



52
53
54
# File 'lib/file/find.rb', line 52

def atime
  @atime
end

#ctimeObject

Limits searches by file change time, where the value you supply is the number of days back from the time that the File::Find#find method was called.



58
59
60
# File 'lib/file/find.rb', line 58

def ctime
  @ctime
end

#filetestObject

An array of two element arrays for storing FileTest methods and their boolean value.



70
71
72
# File 'lib/file/find.rb', line 70

def filetest
  @filetest
end

#followObject

Controls the behavior of how symlinks are followed. If set to true (the default), then follows the file pointed to. If false, it considers the symlink itself.



76
77
78
# File 'lib/file/find.rb', line 76

def follow
  @follow
end

#ftypeObject

Limits searches to specific types of files. The possible values here are those returned by the File.ftype method.



81
82
83
# File 'lib/file/find.rb', line 81

def ftype
  @ftype
end

#groupObject

Limits searches to files that belong to a specific group, where the group can be either a group name or ID.

Not currently supported on MS Windows.



65
66
67
# File 'lib/file/find.rb', line 65

def group
  @group
end

#inumObject

Limits search to a file with a specific inode number. Ignored on MS Windows.



86
87
88
# File 'lib/file/find.rb', line 86

def inum
  @inum
end

Limits search to files with the specified number of links.



90
91
92
# File 'lib/file/find.rb', line 90

def links
  @links
end

#maxdepthObject

Limits search to a maximum depth into the tree relative to the starting search directory.



95
96
97
# File 'lib/file/find.rb', line 95

def maxdepth
  @maxdepth
end

#mindepthObject

Limits searches to a minimum depth into the tree relative to the starting search directory.



100
101
102
# File 'lib/file/find.rb', line 100

def mindepth
  @mindepth
end

#mountObject

Limits searches to the same filesystem as the specified directory. For Windows users, this refers to the volume.



105
106
107
# File 'lib/file/find.rb', line 105

def mount
  @mount
end

#mtimeObject

Limits searches by file modification time, where the value you supply is the number of days back from the time that the File::Find#find method was called.



111
112
113
# File 'lib/file/find.rb', line 111

def mtime
  @mtime
end

#nameObject Also known as: pattern

The name pattern used to limit file searches. The patterns that are legal for Dir.glob are legal here. The default is ‘*’, i.e. everything.



116
117
118
# File 'lib/file/find.rb', line 116

def name
  @name
end

#optionsObject

The list of options passed to the constructor and/or used by the File::Find#find method.



46
47
48
# File 'lib/file/find.rb', line 46

def options
  @options
end

#pathObject

The starting path(s) for the search. The default is the current directory. This can be a single path or an array of paths.



41
42
43
# File 'lib/file/find.rb', line 41

def path
  @path
end

#permObject

Limits searches to files which have permissions that match the octal value that you provide. For purposes of this comparison, only the user, group, and world settings are used. Do not use a leading 0 in the values that you supply, e.g. use 755 not 0755.

You may optionally use symbolic permissions, e.g. “g+rw”, “u=rwx”, etc.

Not currently supported on MS Windows.



127
128
129
# File 'lib/file/find.rb', line 127

def perm
  @perm
end

#previousObject (readonly)

The file that matched previously in the current search.



149
150
151
# File 'lib/file/find.rb', line 149

def previous
  @previous
end

#pruneObject

Skips files or directories that match the string provided as an argument.



131
132
133
# File 'lib/file/find.rb', line 131

def prune
  @prune
end

#sizeObject

If the value passed is an integer, this option limits searches to files that match the size, in bytes, exactly. If a string is passed, you can use the standard comparable operators to match files, e.g. “>= 200” would limit searches to files greater than or equal to 200 bytes.



138
139
140
# File 'lib/file/find.rb', line 138

def size
  @size
end

#userObject

Limits searches to files that belong to a specific user, where the user can be either a user name or an ID.

Not currently supported on MS Windows.



145
146
147
# File 'lib/file/find.rb', line 145

def user
  @user
end

Instance Method Details

#findObject

Executes the find based on the rules you set for the File::Find object. In block form, yields each file in turn that matches the specified rules. In non-block form it will return an array of matches instead.

Example:

rule = File::Find.new(
   :name    => "*.rb",
   :follow  => false,
   :path    => ['/usr/local/lib', '/opt/local/lib']
)

rule.find{ |f|
   puts f
}


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/file/find.rb', line 216

def find
  results = [] unless block_given?
  paths   = @path.is_a?(String) ? [@path] : @path # Ruby 1.9.x compatibility

  if @prune
    prune_regex = Regexp.new(@prune)
  else
    prune_regex = nil
  end

  paths.each{ |path|
    begin
      Dir.foreach(path){ |file|
        next if file == '.'
        next if file == '..'

        if prune_regex
          next if prune_regex.match(file)
        end

        file = File.join(path, file)

        stat_method = @follow ? :stat : :lstat
        # Skip files we cannot access, stale links, etc.
        begin
          stat_info = File.send(stat_method, file)
        rescue Errno::ENOENT, Errno::EACCES
          next
        rescue Errno::ELOOP
          stat_method = :lstat # Handle recursive symlinks
          retry if stat_method.to_s != 'lstat'
        end

        # We need to escape any brackets in the directory name.
        glob = File.join(File.dirname(file).gsub(/([\[\]])/,'\\\\\1'), @name)

        # Dir[] doesn't like backslashes
        if File::ALT_SEPARATOR
          file.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
          glob.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
        end

        if @mount
          next unless stat_info.dev == @filesystem
        end

        if @links
          next unless stat_info.nlink == @links
        end

        if @maxdepth || @mindepth
          file_depth = file.split(File::SEPARATOR).length
          path_depth = @path.split(File::SEPARATOR).length
          depth = file_depth - path_depth

          if @maxdepth && (depth > @maxdepth)
            if File.directory?(file)
              unless paths.include?(file) && depth > @maxdepth
                paths << file
              end
            end

            next
          end

          if @mindepth && (depth < @mindepth)
            if File.directory?(file)
              unless paths.include?(file) && depth < @mindepth
                paths << file
              end
            end

            next
          end
        end

        # Add directories back onto the list of paths to search unless
        # they've already been added
        #
        if stat_info.directory?
          paths << file unless paths.include?(file)
        end

        next unless Dir[glob].include?(file)

        unless @filetest.empty?
          file_test = true

          @filetest.each{ |array|
            meth = array[0]
            bool = array[1]

            unless File.send(meth, file) == bool
              file_test = false
              break
            end
          }

          next unless file_test
        end

        if @atime || @ctime || @mtime
          date1 = Date.parse(Time.now.to_s)

          if @atime
            date2 = Date.parse(stat_info.atime.to_s)
            next unless (date1 - date2).numerator == @atime
          end

          if @ctime
            date2 = Date.parse(stat_info.ctime.to_s)
            next unless (date1 - date2).numerator == @ctime
          end

          if @mtime
            date2 = Date.parse(stat_info.mtime.to_s)
            next unless (date1 - date2).numerator == @mtime
          end
        end

        if @ftype
          next unless File.ftype(file) == @ftype
        end

        if @group
          if @group.is_a?(String)
            next unless get_group(stat_info.gid).name == @group
          else
            next unless stat_info.gid == @group
          end
        end

        unless File::ALT_SEPARATOR
          if @inum
            next unless stat_info.ino == @inum
          end
        end

        # This currently doesn't work on MS Windows, even in limited
        # fashion for 0666 and 0664, because File.stat.mode doesn't
        # return the proper value.
        #
        if @perm
          if @perm.is_a?(String)
            octal_perm = sym2oct(@perm)
            next unless stat_info.mode & octal_perm == octal_perm
          else
            next unless sprintf("%o", stat_info.mode & 07777) == @perm.to_s
          end
        end

        # Allow plain numbers, or strings for comparison operators.
        if @size
          if @size.is_a?(String)
            regex = /^([><=]+)\s*?(\d+)$/
            match = regex.match(@size)

            if match.nil? || match.captures.include?(nil)
              raise ArgumentError, "invalid size string: '#{@size}'"
            end

            operator = match.captures.first.strip
            number   = match.captures.last.strip.to_i

            next unless stat_info.size.send(operator, number)
          else
            next unless stat_info.size == @size
          end
        end

        if @user
          if @user.is_a?(String)
            next unless get_user(stat_info.uid).name == @user
          else
            next unless stat_info.uid == @user
          end
        end

        if block_given?
          yield file
        else
          results << file
        end

        @previous = file unless @previous == file
      }
    rescue Errno::EACCES
      next # Skip inaccessible directories
    end
  }

  block_given? ? nil : results
end