Module: FileUtils

Defined in:
lib/standard/facets/fileutils/wc.rb,
lib/standard/facets/fileutils/ln_r.rb,
lib/standard/facets/fileutils/which.rb,
lib/standard/facets/fileutils/slice.rb,
lib/standard/facets/fileutils/stage.rb,
lib/standard/facets/fileutils/cp_rx.rb,
lib/standard/facets/fileutils/amass.rb,
lib/standard/facets/fileutils/whereis.rb,
lib/standard/facets/fileutils/safe_ln.rb,
lib/standard/facets/fileutils/outofdate.rb

Defined Under Namespace

Modules: DryRun, NoWrite, Verbose Classes: Entry_

Constant Summary

OPT_TABLE =
{}
Win32Exts =
%w{.exe .com .bat .cmd}
LINKING_SUPPORTED =
[true]

Class Method Summary (collapse)

Instance Method Summary (collapse)

Class Method Details

+ (Object) amass(include_globs, exclude_globs = [], ignore = [])

An intergrated glob like method that takes a set of include globs, exclude globs and ignore globs to produce a collection of paths.

The ignore_globs differ from exclude_globs in that they match by the basename of the path rather than the whole pathname.

TODO: Should ignore be based on any portion of the path, not just the basename?



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/standard/facets/fileutils/amass.rb', line 13

def amass(include_globs, exclude_globs=[], ignore=[])

  include_files = [include_globs].flatten.map{ |g| Dir.glob(g) }.flatten.uniq
  exclude_files = [exclude_globs].flatten.map{ |g| Dir.glob(g) }.flatten.uniq

  include_files = include_files.map{ |f| File.directory?(f) ? File.join(f, '**/*') : f } # Recursive!
  exclude_files = exclude_files.map{ |f| File.directory?(f) ? File.join(f, '**/*') : f } # Recursive!

  include_files = include_files.flatten.map{ |g| Dir.glob(g) }.flatten.uniq
  exclude_files = exclude_files.flatten.map{ |g| Dir.glob(g) }.flatten.uniq

  files = include_files - exclude_files

  files = files.reject{ |f| [ignore].flatten.any?{ |x| File.fnmatch?(x, File.basename(f)) } }

  files
end

+ (Object) copy_entryx(src, dest, filter, preserve = false, dereference_root = false, remove_destination = false)

Like FileUtils.copy_entry, but takes a filter proc that can return false to skip a file.

Note that if the filter rejects a subdirectory then everything within that subdirectory is automatically skipped as well.



32
33
34
35
36
37
38
39
40
41
# File 'lib/standard/facets/fileutils/cp_rx.rb', line 32

def copy_entryx(src, dest, filter, preserve = false, dereference_root = false, remove_destination = false)
 Entry_.new(src, nil, dereference_root).traverse do |ent|
  if filter.call(ent.path) then
	  destent = Entry_.new(dest, ent.rel, false)
	  File.unlink destent.path if remove_destination && File.file?(destent.path)
	  ent.copy destent.path
	  ent.(destent.path) if preserve
  end
 end
end

+ (Object) cp_rx(src, dest, options = {}, &filter)

Like FileUtils.cp_r, but takes a filter proc that can return false to skip a file:

cp_rx "bigDirectoryTree", "dest", {:noop => true} do |name|
   /dontCopyThis$/.match(name)
end

Note that if the filter rejects a subdirectory then everything within that subdirectory is automatically skipped as well.



16
17
18
19
20
21
22
23
24
25
# File 'lib/standard/facets/fileutils/cp_rx.rb', line 16

def cp_rx(src, dest, options = {}, &filter)
 fu_check_options(options, OPT_TABLE['cp_r'])
  if options[:verbose]
   fu_output_message("cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}")
  end
 return if options[:noop]
 fu_each_src_dest(src, dest) do |s, d|
  copy_entryx(s, d, filter, options[:preserve], options[:dereference_root], options[:remove_destination])
 end
end

+ (Object) head(filename, lines)

In block form, yields the first number of lines of file filename. In non-block form, it returns an array of the first number of lines:

# Returns first 10 lines of 'myfile'
FileUtils.head("myfile", 10)


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/standard/facets/fileutils/slice.rb', line 25

def head(filename,lines) #:yield:
  a = []
  IO.foreach(filename){|line|
      break if lines <= 0
      lines -= 1
      if block_given?
        yield line
      else
        a << line
      end
  }
  return a.empty? ? nil : a
end

+ (Object) link_entry(src, dest, dereference_root = false, remove_destination = false)



55
56
57
58
59
60
61
# File 'lib/standard/facets/fileutils/ln_r.rb', line 55

def link_entry(src, dest, dereference_root = false, remove_destination = false)
  Entry_.new(src, nil, dereference_root).traverse do |ent|
    destent = Entry_.new(dest, ent.rel, false)
    File.unlink destent.path if remove_destination && File.file?(destent.path)
    ent.link destent.path
  end
end

+ (Object) ln_r(src, dest, options = {})



29
30
31
32
33
34
35
36
37
38
# File 'lib/standard/facets/fileutils/ln_r.rb', line 29

def ln_r(src, dest, options = {})
  fu_check_options options, OPT_TABLE['ln_r']
  fu_output_message "ln -r#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  return if options[:noop]
  options = options.dup
  options[:dereference_root] = true unless options.key?(:dereference_root)
  fu_each_src_dest(src, dest) do |s, d|
    link_entry s, d, options[:dereference_root], options[:remove_destination]
  end
end

+ (Boolean) outofdate?(path, *sources) Also known as: out_of_date?

The opposite of #uptodate?

Returns:

  • (Boolean)


7
8
9
10
# File 'lib/standard/facets/fileutils/outofdate.rb', line 7

def outofdate?(path, *sources)
  #return true unless File.exist?(path)
  ! uptodate?(path, sources.flatten)
end

+ (Object) safe_ln(*args)

Attempt to do a normal file link, but fall back to a copy if the link fails.

CREDIT: Jim Weirich



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/standard/facets/fileutils/safe_ln.rb', line 13

def safe_ln(*args)
  unless LINKING_SUPPORTED[0]
    cp(*args)
  else
    begin
      ln(*args)
    rescue Errno::EOPNOTSUPP
      LINKING_SUPPORTED[0] = false
      cp(*args)
    end
  end
end

+ (Object) slice(filename, from, to)

In block form, yields lines from-to. In non-block form, returns an array of lines from-to:

# Returns lines 8-12 of 'myfile'
FileUtils.body("myfile",8,12)

CREDIT Shashank Date, via Daniel Berger.



15
16
17
# File 'lib/standard/facets/fileutils/slice.rb', line 15

def slice(filename,from,to) #:yield:
  IO.readlines(filename)[from-1..to-1]
end

+ (Object) stage(stage_directory, source_directory, files, options = {})

Stage by hard linking included files to a stage directory.

stage_directory - Where to stage the files source_directory - Where to find files to stage files - Files to link in stage relative to source

TODO: Rename to #link_stage or something less likely to name clash? TODO: Add options for :verbose, :noop and :dryrun ?



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/standard/facets/fileutils/stage.rb', line 15

def stage(stage_directory, source_directory, files, options={})
  return stage_directory if options[:noop] || options[:dryrun]

  stage_directory, source_directory = stage_directory.to_s, source_directory.to_s
  ## ensure existance of staging area
  rm_r(stage_directory) if File.directory?(stage_directory)
  mkdir_p(stage_directory)
  ## link files into staging area
  files.each do |f|
    src  = File.join(source_directory, f)
    file = File.join(stage_directory, f)
    if File.directory?(src)
      mkdir_p(file) unless File.exist?(file)
    else
      fdir = File.dirname(file)
      mkdir_p(fdir) unless File.exist?(fdir)
      unless File.exist?(file) and File.mtime(file) >= File.mtime(src)
        ln(src, file) #safe_ln ?
      end
    end
  end
  return stage_directory
end

+ (Object) tail(filename, lines)

In block form, yields the last number of lines of file filename. In non-block form, it returns the lines as an array.

Note that this method slurps the entire file, so I don't recommend it for very large files. If you want an advanced form of tail, I suggest using file-tail, by Florian Frank (available on the RAA):

# Returns last 3 lines of 'myfile'
FileUtils.tail("myfile",3)

And no tail -f.



50
51
52
# File 'lib/standard/facets/fileutils/slice.rb', line 50

def tail(filename,lines) #:yield
  IO.readlines(filename).reverse[0..lines-1].reverse
end

+ (Object) wc(filename, option = 'all')

With no arguments, returns a four element array consisting of the number of bytes, characters, words and lines in filename, respectively.

Valid options are bytes, characters (or just 'chars'), words and lines:

# Return the number of words in 'myfile'
FileUtils.wc("myfile",'words')

CREDIT: Daniel J. Berger



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/standard/facets/fileutils/wc.rb', line 17

def wc(filename,option='all')
  option.downcase!
  valid = %w/all bytes characters chars lines words/

  unless valid.include?(option)
      raise "Invalid option: '#{option}'"
  end

  n = 0
  if option == 'lines'
      IO.foreach(filename){ n += 1 }
      return n
  elsif option == 'bytes'
      File.open(filename){ |f|
        f.each_byte{ n += 1 }
      }
      return n
  elsif option == 'characters' || option == 'chars'
      File.open(filename){ |f|
        while f.getc
            n += 1
        end
      }
      return n
  elsif option == 'words'
      IO.foreach(filename){ |line|
        n += line.split.length
      }
      return n
  else
      bytes,chars,lines,words = 0,0,0,0
      IO.foreach(filename){ |line|
        lines += 1
        words += line.split.length
        chars += line.split('').length
      }
      File.open(filename){ |f|
        while f.getc
            bytes += 1
        end
      }
      return [bytes,chars,words,lines]
  end
end

+ (Object) whereis(prog, path = ENV['PATH'])

In block form, yields each ((program)) within ((path)). In non-block form, returns an array of each ((program)) within ((path)). Returns ((nil)) if not found.

On the MS Windows platform, it looks for executables ending with .exe, .bat and .com, which you may optionally include in the program name:

FileUtils.whereis("ruby")  #=> ['/usr/local/bin/ruby','/opt/bin/ruby']

CREDIT: Daniel J. Berger



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/standard/facets/fileutils/whereis.rb', line 25

def whereis(prog, path=ENV['PATH']) #:yield:
  dirs = []
  path.split(File::PATH_SEPARATOR).each{|dir|
      # Windows checks against specific extensions
      if File::ALT_SEPARATOR
        if prog.include?('.')
            f = File.join(dir,prog)
            if File.executable?(f) && !File.directory?(f)
              if block_given?
                  yield f.gsub(/\//,'\\')
              else
                  dirs << f.gsub(/\//,'\\')
              end
            end
        else
            Win32Exts.find_all{|ext|
              f = File.join(dir,prog+ext)
              if File.executable?(f) && !File.directory?(f)
                  if block_given?
                    yield f.gsub(/\//,'\\')
                  else
                    dirs << f.gsub(/\//,'\\')
                  end
              end
            }
        end
      else
        f = File.join(dir,prog)
        # Avoid /usr/lib/ruby, for example
        if File.executable?(f) && !File.directory?(f)
            if block_given?
              yield f
            else
              dirs << f
            end
        end
      end
  }
  dirs.empty? ? nil : dirs
end

+ (Object) which(prog, path = ENV['PATH'])

Looks for the first occurrence of program within path.

On the MS Windows platform, it looks for executables ending with .exe, .bat and .com, which you may optionally include in the program name. Returns nil if not found.

CREDIT: Daniel J. Berger, Michael Granger

– The which() method was adopted from Daniel J. Berger, via PTools which in in turn was adopted fromt the FileWhich code posted by Michael Granger on www.rubygarden.org. ++



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/standard/facets/fileutils/which.rb', line 27

def which(prog, path=ENV['PATH'])
  path.split(File::PATH_SEPARATOR).each {|dir|
    # Windows checks against specific extensions
    if File::ALT_SEPARATOR
      ext = Win32Exts.find{|ext|
        if prog.include?('.') # Assume extension already included
          f = File.join(dir,prog)
        else
          f = File.join(dir,prog+ext)
        end
        File.executable?(f) && !File.directory?(f)
      }
      if ext
        # Use backslashes, not forward slashes
        if prog.include?('.') # Assume extension already included
          f = File.join( dir, prog ).gsub(/\//,'\\')
        else
          f = File.join( dir, prog + ext ).gsub(/\//,'\\')
        end
        return f
      end
    else
      f = File.join(dir,prog)
      # Avoid /usr/lib/ruby, for example
      if File.executable?(f) && !File.directory?(f)
        return File::join( dir, prog )
      end
    end
  }
  nil
end

Instance Method Details

- (Object) amass(include_globs, exclude_globs = [], ignore = []) (private)

An intergrated glob like method that takes a set of include globs, exclude globs and ignore globs to produce a collection of paths.

The ignore_globs differ from exclude_globs in that they match by the basename of the path rather than the whole pathname.

TODO: Should ignore be based on any portion of the path, not just the basename?



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/standard/facets/fileutils/amass.rb', line 13

def amass(include_globs, exclude_globs=[], ignore=[])

  include_files = [include_globs].flatten.map{ |g| Dir.glob(g) }.flatten.uniq
  exclude_files = [exclude_globs].flatten.map{ |g| Dir.glob(g) }.flatten.uniq

  include_files = include_files.map{ |f| File.directory?(f) ? File.join(f, '**/*') : f } # Recursive!
  exclude_files = exclude_files.map{ |f| File.directory?(f) ? File.join(f, '**/*') : f } # Recursive!

  include_files = include_files.flatten.map{ |g| Dir.glob(g) }.flatten.uniq
  exclude_files = exclude_files.flatten.map{ |g| Dir.glob(g) }.flatten.uniq

  files = include_files - exclude_files

  files = files.reject{ |f| [ignore].flatten.any?{ |x| File.fnmatch?(x, File.basename(f)) } }

  files
end

- (Object) copy_entryx(src, dest, filter, preserve = false, dereference_root = false, remove_destination = false) (private)

Like FileUtils.copy_entry, but takes a filter proc that can return false to skip a file.

Note that if the filter rejects a subdirectory then everything within that subdirectory is automatically skipped as well.



32
33
34
35
36
37
38
39
40
41
# File 'lib/standard/facets/fileutils/cp_rx.rb', line 32

def copy_entryx(src, dest, filter, preserve = false, dereference_root = false, remove_destination = false)
 Entry_.new(src, nil, dereference_root).traverse do |ent|
  if filter.call(ent.path) then
	  destent = Entry_.new(dest, ent.rel, false)
	  File.unlink destent.path if remove_destination && File.file?(destent.path)
	  ent.copy destent.path
	  ent.(destent.path) if preserve
  end
 end
end

- (Object) cp_rx(src, dest, options = {}, &filter) (private)

Like FileUtils.cp_r, but takes a filter proc that can return false to skip a file:

cp_rx "bigDirectoryTree", "dest", {:noop => true} do |name|
   /dontCopyThis$/.match(name)
end

Note that if the filter rejects a subdirectory then everything within that subdirectory is automatically skipped as well.



16
17
18
19
20
21
22
23
24
25
# File 'lib/standard/facets/fileutils/cp_rx.rb', line 16

def cp_rx(src, dest, options = {}, &filter)
 fu_check_options(options, OPT_TABLE['cp_r'])
  if options[:verbose]
   fu_output_message("cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}")
  end
 return if options[:noop]
 fu_each_src_dest(src, dest) do |s, d|
  copy_entryx(s, d, filter, options[:preserve], options[:dereference_root], options[:remove_destination])
 end
end

- (Object) head(filename, lines) (private)

In block form, yields the first number of lines of file filename. In non-block form, it returns an array of the first number of lines:

# Returns first 10 lines of 'myfile'
FileUtils.head("myfile", 10)


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/standard/facets/fileutils/slice.rb', line 25

def head(filename,lines) #:yield:
  a = []
  IO.foreach(filename){|line|
      break if lines <= 0
      lines -= 1
      if block_given?
        yield line
      else
        a << line
      end
  }
  return a.empty? ? nil : a
end

- (Object) link_entry(src, dest, dereference_root = false, remove_destination = false) (private)



55
56
57
58
59
60
61
# File 'lib/standard/facets/fileutils/ln_r.rb', line 55

def link_entry(src, dest, dereference_root = false, remove_destination = false)
  Entry_.new(src, nil, dereference_root).traverse do |ent|
    destent = Entry_.new(dest, ent.rel, false)
    File.unlink destent.path if remove_destination && File.file?(destent.path)
    ent.link destent.path
  end
end

- (Object) ln_r(src, dest, options = {}) (private)



29
30
31
32
33
34
35
36
37
38
# File 'lib/standard/facets/fileutils/ln_r.rb', line 29

def ln_r(src, dest, options = {})
  fu_check_options options, OPT_TABLE['ln_r']
  fu_output_message "ln -r#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  return if options[:noop]
  options = options.dup
  options[:dereference_root] = true unless options.key?(:dereference_root)
  fu_each_src_dest(src, dest) do |s, d|
    link_entry s, d, options[:dereference_root], options[:remove_destination]
  end
end

- (Object) out_of_date? (private)

Alias for #outofdate?



13
# File 'lib/standard/facets/fileutils/outofdate.rb', line 13

alias_method :out_of_date?, :outofdate?

- (Object) outofdate?(path, *sources) (private)

The opposite of #uptodate?



7
8
9
10
# File 'lib/standard/facets/fileutils/outofdate.rb', line 7

def outofdate?(path, *sources)
  #return true unless File.exist?(path)
  ! uptodate?(path, sources.flatten)
end

- (Object) safe_ln(*args) (private)

Attempt to do a normal file link, but fall back to a copy if the link fails.

CREDIT: Jim Weirich



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/standard/facets/fileutils/safe_ln.rb', line 13

def safe_ln(*args)
  unless LINKING_SUPPORTED[0]
    cp(*args)
  else
    begin
      ln(*args)
    rescue Errno::EOPNOTSUPP
      LINKING_SUPPORTED[0] = false
      cp(*args)
    end
  end
end

- (Object) slice(filename, from, to) (private)

In block form, yields lines from-to. In non-block form, returns an array of lines from-to:

# Returns lines 8-12 of 'myfile'
FileUtils.body("myfile",8,12)

CREDIT Shashank Date, via Daniel Berger.



15
16
17
# File 'lib/standard/facets/fileutils/slice.rb', line 15

def slice(filename,from,to) #:yield:
  IO.readlines(filename)[from-1..to-1]
end

- (Object) stage(stage_directory, source_directory, files, options = {}) (private)

Stage by hard linking included files to a stage directory.

stage_directory - Where to stage the files source_directory - Where to find files to stage files - Files to link in stage relative to source

TODO: Rename to #link_stage or something less likely to name clash? TODO: Add options for :verbose, :noop and :dryrun ?



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/standard/facets/fileutils/stage.rb', line 15

def stage(stage_directory, source_directory, files, options={})
  return stage_directory if options[:noop] || options[:dryrun]

  stage_directory, source_directory = stage_directory.to_s, source_directory.to_s
  ## ensure existance of staging area
  rm_r(stage_directory) if File.directory?(stage_directory)
  mkdir_p(stage_directory)
  ## link files into staging area
  files.each do |f|
    src  = File.join(source_directory, f)
    file = File.join(stage_directory, f)
    if File.directory?(src)
      mkdir_p(file) unless File.exist?(file)
    else
      fdir = File.dirname(file)
      mkdir_p(fdir) unless File.exist?(fdir)
      unless File.exist?(file) and File.mtime(file) >= File.mtime(src)
        ln(src, file) #safe_ln ?
      end
    end
  end
  return stage_directory
end

- (Object) tail(filename, lines) (private)

In block form, yields the last number of lines of file filename. In non-block form, it returns the lines as an array.

Note that this method slurps the entire file, so I don't recommend it for very large files. If you want an advanced form of tail, I suggest using file-tail, by Florian Frank (available on the RAA):

# Returns last 3 lines of 'myfile'
FileUtils.tail("myfile",3)

And no tail -f.



50
51
52
# File 'lib/standard/facets/fileutils/slice.rb', line 50

def tail(filename,lines) #:yield
  IO.readlines(filename).reverse[0..lines-1].reverse
end

- (Object) up_to_date? (private)

Alias for #uptodate?



16
# File 'lib/standard/facets/fileutils/outofdate.rb', line 16

alias_method :up_to_date?, :uptodate?

- (Object) wc(filename, option = 'all') (private)

With no arguments, returns a four element array consisting of the number of bytes, characters, words and lines in filename, respectively.

Valid options are bytes, characters (or just 'chars'), words and lines:

# Return the number of words in 'myfile'
FileUtils.wc("myfile",'words')

CREDIT: Daniel J. Berger



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/standard/facets/fileutils/wc.rb', line 17

def wc(filename,option='all')
  option.downcase!
  valid = %w/all bytes characters chars lines words/

  unless valid.include?(option)
      raise "Invalid option: '#{option}'"
  end

  n = 0
  if option == 'lines'
      IO.foreach(filename){ n += 1 }
      return n
  elsif option == 'bytes'
      File.open(filename){ |f|
        f.each_byte{ n += 1 }
      }
      return n
  elsif option == 'characters' || option == 'chars'
      File.open(filename){ |f|
        while f.getc
            n += 1
        end
      }
      return n
  elsif option == 'words'
      IO.foreach(filename){ |line|
        n += line.split.length
      }
      return n
  else
      bytes,chars,lines,words = 0,0,0,0
      IO.foreach(filename){ |line|
        lines += 1
        words += line.split.length
        chars += line.split('').length
      }
      File.open(filename){ |f|
        while f.getc
            bytes += 1
        end
      }
      return [bytes,chars,words,lines]
  end
end

- (Object) whereis(prog, path = ENV['PATH']) (private)

In block form, yields each ((program)) within ((path)). In non-block form, returns an array of each ((program)) within ((path)). Returns ((nil)) if not found.

On the MS Windows platform, it looks for executables ending with .exe, .bat and .com, which you may optionally include in the program name:

FileUtils.whereis("ruby")  #=> ['/usr/local/bin/ruby','/opt/bin/ruby']

CREDIT: Daniel J. Berger



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/standard/facets/fileutils/whereis.rb', line 25

def whereis(prog, path=ENV['PATH']) #:yield:
  dirs = []
  path.split(File::PATH_SEPARATOR).each{|dir|
      # Windows checks against specific extensions
      if File::ALT_SEPARATOR
        if prog.include?('.')
            f = File.join(dir,prog)
            if File.executable?(f) && !File.directory?(f)
              if block_given?
                  yield f.gsub(/\//,'\\')
              else
                  dirs << f.gsub(/\//,'\\')
              end
            end
        else
            Win32Exts.find_all{|ext|
              f = File.join(dir,prog+ext)
              if File.executable?(f) && !File.directory?(f)
                  if block_given?
                    yield f.gsub(/\//,'\\')
                  else
                    dirs << f.gsub(/\//,'\\')
                  end
              end
            }
        end
      else
        f = File.join(dir,prog)
        # Avoid /usr/lib/ruby, for example
        if File.executable?(f) && !File.directory?(f)
            if block_given?
              yield f
            else
              dirs << f
            end
        end
      end
  }
  dirs.empty? ? nil : dirs
end

- (Object) which(prog, path = ENV['PATH']) (private)

Looks for the first occurrence of program within path.

On the MS Windows platform, it looks for executables ending with .exe, .bat and .com, which you may optionally include in the program name. Returns nil if not found.

CREDIT: Daniel J. Berger, Michael Granger

– The which() method was adopted from Daniel J. Berger, via PTools which in in turn was adopted fromt the FileWhich code posted by Michael Granger on www.rubygarden.org. ++



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/standard/facets/fileutils/which.rb', line 27

def which(prog, path=ENV['PATH'])
  path.split(File::PATH_SEPARATOR).each {|dir|
    # Windows checks against specific extensions
    if File::ALT_SEPARATOR
      ext = Win32Exts.find{|ext|
        if prog.include?('.') # Assume extension already included
          f = File.join(dir,prog)
        else
          f = File.join(dir,prog+ext)
        end
        File.executable?(f) && !File.directory?(f)
      }
      if ext
        # Use backslashes, not forward slashes
        if prog.include?('.') # Assume extension already included
          f = File.join( dir, prog ).gsub(/\//,'\\')
        else
          f = File.join( dir, prog + ext ).gsub(/\//,'\\')
        end
        return f
      end
    else
      f = File.join(dir,prog)
      # Avoid /usr/lib/ruby, for example
      if File.executable?(f) && !File.directory?(f)
        return File::join( dir, prog )
      end
    end
  }
  nil
end