On Tue, 1 Nov 2005, Robert Klemme wrote: > Hugh Sasse <hgs / dmu.ac.uk> wrote: > > begin > > #... > > rescue => e > > #... > > end > > > > will trap e if it is a StandardError. SystemCallErrrors are > > supposed to handle Errorcodes from the OS. All of these are > > subclasses of Exception. So why do I get this failure under Cygwin: > > > > $ ruby BACKUP.RB "C:\\" "D:\\buzz_c" > > cp -rp C:\ D:\buzz_c > > /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `initialize': Device or > > resource busy - C:\/WINDOWS/WIN386.SWP (Errno::EBUSY) > > from /usr/lib/ruby/1.8/new_fileutils.rb:1251:in `copy_file' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1221:in `copy' > > from /usr/lib/ruby/1.8/new_fileutils.rb:455:in `copy_entry' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1314:in `traverse' > > from /usr/lib/ruby/1.8/new_fileutils.rb:453:in `copy_entry' > > from /usr/lib/ruby/1.8/new_fileutils.rb:424:in `cp_r' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1385:in > > `fu_each_src_dest' from > > /usr/lib/ruby/1.8/new_fileutils.rb:1401:in `fu_each_src_dest0' > > from /usr/lib/ruby/1.8/new_fileutils.rb:1383:in > > `fu_each_src_dest' from > > /usr/lib/ruby/1.8/new_fileutils.rb:422:in `cp_r' from BACKUP.RB:27 > > > > hgs@buzz ~/downloads > > > > when my modified FileUtils.cp_r has > > > > begin > > copy_entry ... > > rescue Exception => e > > logger.error("backup"){"Error was #{e}") > > end > > > > (essentially. Theres a bit more to it than that, but the details > > shouldn't matter for my question.) So why can't I rescue it? (I'm > > trying to log, and skip files I can't backup so at least I get most > > of the files, and know which ones I have not.) > > Maybe it's in another thread. Or your code is actually not between "begin" > and "rescue" but outside of that. there's no threading in there, and I'm pretty certain it is within that, because it is in the call to copy_entry > > Kind regards > > robert > The modified fileutils is (heavily pruned) below I've changed cp_r. Hugh # # = fileutils.rb # # Copyright (c) 2000-2005 Minero Aoki <aamine / loveruby.net> # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # # == module FileUtils # # Namespace for several file utility methods for copying, moving, removing, etc. # # === Module Functions # # [...] # cp_r(src, dest, options) {|s,d,e|...} # cp_r(list, dir, options) {|s,d,e|...} # [...] # # The <tt>options</tt> parameter is a hash of options, taken from the list # <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>. # <tt>:noop</tt> means that no changes are made. The other two are obvious. # Each method documents the options that it honours. # # All methods that have the concept of a "source" file or directory can take # either one file or a list of files in that argument. See the method # documentation for examples. # # There are some `low level' methods, which do not accept any option: # # copy_entry(src, dest, preserve = false, dereference = false) # copy_file(src, dest, preserve = false, dereference = true) # [...] # # == module FileUtils::Verbose # # This module has all methods of FileUtils module, but it outputs messages # before acting. This equates to passing the <tt>:verbose</tt> flag to methods # in FileUtils. # # == module FileUtils::NoWrite # # This module has all methods of FileUtils module, but never changes # files/directories. This equates to passing the <tt>:noop</tt> flag to methods # in FileUtils. # # == module FileUtils::DryRun # # This module has all methods of FileUtils module, but never changes # files/directories. This equates to passing the <tt>:noop</tt> and # <tt>:verbose</tt> flags to methods in FileUtils. # module FileUtils def self.private_module_function(name) #:nodoc: module_function name private_class_method name end # This hash table holds command options. OPT_TABLE = {} #:nodoc: internal use only # [...] def fu_mkdir(path, mode) #:nodoc: path = path.sub(%r</\z>, '') if mode Dir.mkdir path, mode File.chmod mode, path else Dir.mkdir path end end private_module_function :fu_mkdir # [...] # # Options: preserve noop verbose dereference_root # # Copies +src+ to +dest+. If +src+ is a directory, this method copies # all its contents recursively. If +dest+ is a directory, copies # +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.cp_r 'lib/', site_ruby + '/mylib' # # # Examples of copying several files to target directory. # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail' # FileUtils.cp_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.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest, # # but this doesn't. # def cp_r(src, dest, options = {}) fu_check_options options, :preserve, :noop, :verbose, :dereference_root fu_output_message "cp -r#{options[:preserve] ? 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] return if options[:noop] options[:dereference_root] = true unless options.key?(:dereference_root) fu_each_src_dest(src, dest) do |s, d| begin copy_entry s, d, options[:preserve], options[:dereference_root] rescue Exception => e stop = true if block_given? stop = yield s,d,e end raise if stop end end end module_function :cp_r OPT_TABLE['cp_r'] = %w( noop verbose preserve dereference_root ) # # Copies a file system entry +src+ to +dest+. # If +src+ is a directory, this method copies its contents recursively. # This method preserves file types, c.f. symlink, directory... # (FIFO, device files and etc. are not supported yet) # # Both of +src+ and +dest+ must be a path name. # +src+ must exist, +dest+ must not exist. # # If +preserve+ is true, this method preserves owner, group, permissions # and modified time. # # If +dereference_root+ is true, this method dereference tree root. # def copy_entry(src, dest, preserve = false, dereference_root = false) Entry_.new(src, nil, dereference_root).traverse do |ent| destent = Entry_.new(dest, ent.rel, false) ent.copy destent.path ent.copy_metadata destent.path if preserve end end module_function :copy_entry # # Copies file contents of +src+ to +dest+. # Both of +src+ and +dest+ must be a path name. # def copy_file(src, dest, preserve = false, dereference = true) ent = Entry_.new(src, nil, dereference) ent.copy_file dest ent.copy_metadata dest if preserve end module_function :copy_file # [...] class Entry_ #:nodoc: internal use only include StreamUtils_ def initialize(a, b = nil, deref = false) @prefix = @rel = @path = nil if b @prefix = a @rel = b else @path = a end @deref = deref @stat = nil @lstat = nil end def inspect "\#<#{self.class} #{path()}>" end def path if @path @path.to_str else join(@prefix, @rel) end end def prefix @prefix || @path end def rel @rel end def dereference? @deref end def exist? lstat! ? true : false end def file? s = lstat! s and s.file? end def directory? s = lstat! s and s.directory? end def symlink? s = lstat! s and s.symlink? end def chardev? s = lstat! s and s.chardev? end def blockdev? s = lstat! s and s.blockdev? end def socket? s = lstat! s and s.socket? end def pipe? s = lstat! s and s.pipe? end S_IF_DOOR = 0xD000 def door? s = lstat! s and (s.mode & 0xF000 == S_IF_DOOR) end def entries Dir.entries(path())\ .reject {|n| n == '.' or n == '..' }\ .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } end def stat return @stat if @stat if lstat() and lstat().symlink? @stat = File.stat(path()) else @stat = lstat() end @stat end def stat! return @stat if @stat if lstat! and lstat!.symlink? @stat = File.stat(path()) else @stat = lstat! end @stat rescue SystemCallError nil end def lstat if dereference? @lstat ||= File.stat(path()) else @lstat ||= File.lstat(path()) end end def lstat! lstat() rescue SystemCallError nil end def chmod(mode) if symlink? File.lchmod mode, path() if have_lchmod? else File.chmod mode, path() end end def chown(uid, gid) if symlink? File.lchown uid, gid, path() if have_lchown? else File.chown uid, gid, path() end end def copy(dest) case when file? copy_file dest when directory? begin Dir.mkdir dest rescue raise unless File.directory?(dest) end when symlink? File.symlink File.readlink(path()), dest when chardev? raise "cannot handle device file" unless File.respond_to?(:mknod) mknod dest, ?c, 0666, lstat().rdev when blockdev? raise "cannot handle device file" unless File.respond_to?(:mknod) mknod dest, ?b, 0666, lstat().rdev when socket? raise "cannot handle socket" unless File.respond_to?(:mknod) mknod dest, nil, lstat().mode, 0 when pipe? raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) mkfifo dest, 0666 when door? raise "cannot handle door: #{path()}" else raise "unknown file type: #{path()}" end end def copy_file(dest) st = stat() File.open(path(), 'rb') {|r| File.open(dest, 'wb', st.mode) {|w| fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize()) } } end def copy_metadata(path) st = lstat() File.utime st.atime, st.mtime, path begin File.chown st.uid, st.gid, path rescue Errno::EPERM # clear setuid/setgid File.chmod st.mode & 01777, path else File.chmod st.mode, path end end # [...] def platform_support return yield unless fu_windows? first_time_p = true begin yield rescue Errno::ENOENT raise rescue => err if first_time_p first_time_p = false begin File.chmod 0700, path() # Windows does not have symlink retry rescue SystemCallError end end raise err end end def preorder_traverse stack = [self] while ent = stack.pop yield ent stack.concat ent.entries.reverse if ent.directory? end end alias traverse preorder_traverse def postorder_traverse if directory? entries().each do |ent| ent.postorder_traverse do |e| yield e end end end yield self end # [...] def join(dir, base) return dir.to_str if not base or base == '.' return base.to_str if not dir or dir == '.' File.join(dir, base) end end # class Entry_ def fu_list(arg) #:nodoc: [arg].flatten.map {|path| path.to_str } end private_module_function :fu_list def fu_each_src_dest(src, dest) #:nodoc: fu_each_src_dest0(src, dest) do |s, d| raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) yield s, d end end private_module_function :fu_each_src_dest def fu_each_src_dest0(src, dest) #:nodoc: if src.is_a?(Array) src.each do |s| s = s.to_str yield s, File.join(dest, File.basename(s)) end else src = src.to_str if File.directory?(dest) yield src, File.join(dest, File.basename(src)) else yield src, dest.to_str end end end private_module_function :fu_each_src_dest0 def fu_same?(a, b) #:nodoc: if fu_have_st_ino? st1 = File.stat(a) st2 = File.stat(b) st1.dev == st2.dev and st1.ino == st2.ino else File.expand_path(a) == File.expand_path(b) end rescue Errno::ENOENT return false end private_module_function :fu_same? def fu_have_st_ino? #:nodoc: not fu_windows? end private_module_function :fu_have_st_ino? def fu_check_options(options, *optdecl) #:nodoc: h = options.dup optdecl.each do |name| h.delete name end raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? end private_module_function :fu_check_options def fu_update_option(args, new) #:nodoc: if args.last.is_a?(Hash) args[-1] = args.last.dup.update(new) else args.push new end args end private_module_function :fu_update_option @fileutils_output = $stderr @fileutils_label = '' def fu_output_message(msg) #:nodoc: @fileutils_output ||= $stderr @fileutils_label ||= '' @fileutils_output.puts @fileutils_label + msg end private_module_function :fu_output_message # # Returns an Array of method names which have any options. # # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] # def FileUtils.commands OPT_TABLE.keys end # # Returns an Array of option names. # # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] # def FileUtils.options OPT_TABLE.values.flatten.uniq end # # Returns true if the method +mid+ have an option +opt+. # # p FileUtils.have_option?(:cp, :noop) #=> true # p FileUtils.have_option?(:rm, :force) #=> true # p FileUtils.have_option?(:rm, :perserve) #=> false # def FileUtils.have_option?(mid, opt) li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" li.include?(opt.to_s) end # # Returns an Array of option names of the method +mid+. # # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] # def FileUtils.options_of(mid) OPT_TABLE[mid.to_s] end # [...] end