# module FileUtils

### Constants

LINKING_SUPPORTED
OPT_TABLE
Win32Exts

### Public Instance Methods

amass(include_globs, exclude_globs=[], ignore=[]) click to toggle source

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?

    # File lib/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

copy_entryx(src, dest, filter, preserve = false, dereference_root = false, remove_destination = false) click to toggle source

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.

    # File lib/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.copy_metadata(destent.path) if preserve
                    end
            end
    end

cp_rx(src, dest, options = {}, &filter) click to toggle source

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.

    # File lib/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

head(filename,lines) { || ... } click to toggle source

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)

    # File lib/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

link_entry(src, dest, dereference_root = false, remove_destination = false) click to toggle source

Hard links a file system entry `src` to `dest`. If `src` is a directory, this method links its contents recursively.

Both of `src` and `dest` must be a path name. `src` must exist, `dest` must not exist.

If `dereference_root` is true, this method dereference tree root.

If `remove_destination` is true, this method removes each destination file before copy.

    # File lib/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

ln_r(src, dest, options = {}) click to toggle source

Options: noop verbose dereference_root remove_destination

Hard link `src` to `dest`. If `src` is a directory, this method links all its contents recursively. If `dest` is a directory, links `src` to `dest/src`.

`src` can be a list of files.

    # Installing ruby library "mylib" under the site_ruby
    FileUtils.rm_r site_ruby + '/mylib', :force
    FileUtils.ln_r 'lib/', site_ruby + '/mylib'

    # Examples of copying several files to target directory.
    FileUtils.ln_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
    FileUtils.ln_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true

    # If you want to copy all contents of a directory instead of the
    # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
    # use following code.
    FileUtils.ln_r 'src/.', 'dest'     # cp_r('src', 'dest') makes src/dest,
                                       # but this doesn't.

TODO: Why –remove-destination and not just –force?

    # File lib/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

out_of_date?(path, *sources)

Alias for outofdate?

Alias for: outofdate?
outofdate?(path, *sources) click to toggle source

The opposite of uptodate?

    # File lib/facets/fileutils/outofdate.rb, line 7
    def outofdate?(path, *sources)
      #return true unless File.exist?(path)
      ! uptodate?(path, sources.flatten)
    end

Also aliased as: out_of_date?
safe_ln(*args) click to toggle source

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

CREDIT: Jim Weirich

    # File lib/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

slice(filename,from,to) { || ... } click to toggle source

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.

    # File lib/facets/fileutils/slice.rb, line 15
    def slice(filename,from,to) #:yield:
      IO.readlines(filename)[from-1..to-1]
    end

stage(stage_directory, source_directory, files, options={}) click to toggle source

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 ?

    # File lib/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

tail(filename,lines) click to toggle source

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.

    # File lib/facets/fileutils/slice.rb, line 50
    def tail(filename,lines) #:yield
      IO.readlines(filename).reverse[0..lines-1].reverse
    end

wc(filename,option='all') click to toggle source

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

    # File lib/facets/fileutils/wc.rb, line 17
    def wc(filename,option='all')
      option.downcase!
      valid = %wall 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

whereis(prog, path=ENV['PATH']) { || ... } click to toggle source

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

    # File lib/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

which(prog, path=ENV['PATH']) click to toggle source

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

    # File lib/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