如何处理Find.find中的异常

时间:2014-11-07 16:01:11

标签: ruby windows

我处理文件和目录,查找每个目录中最新修改的文​​件。我已经使用的代码但是,对于Ruby来说,我很难正确处理错误。

我使用Find.find来获取递归目录列表,为每个目录调用我自己的函数newestFile

Find.find(ARGV[0]) { |f| 
  if File.directory?(f)
    newestFile(f)
  end
}

在目录树中有我无权访问的文件夹,所以我想忽略它们并继续下一步,但我看不到如何将异常处理合并到Find.find& #34;环路"

我试图在块周围放置begin..rescue..end,但这不允许我继续处理循环。

我也发现了这个问题:How to continue processing a block in Ruby after an exception?但是它处理循环中的错误。我试图从Find.find中发生的异常块之外的错误中恢复。

这里是错误的堆栈跟踪:

PS D:\dev\ruby\> ruby .\findrecent.rb "M:/main/*"
C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `open': Invalid argument - M:/main/<A FOLDER I CAN'T ACCESS> (Errno::EINVAL)
        from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `entries'
        from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `block in find'
        from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `catch'
        from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `find'
        from ./findrecent.rb:17:in `<main>'

如何为此代码添加异常处理?

我查看了生成异常的代码,该方法包含以下块:

if s.directory? then
  begin
    fs = Dir.entries(file)
  rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
    next
  end
  ... more code

执行可怕的黑客攻击我将Errno::EINVAL添加到rescue错误列表中。我的代码现在执行并遍历所有文件夹,但我不能在Ruby库代码中保留该更改。

内部find正在使用Dir.entries,因此我可能需要重写我的代码来自行处理文件夹,而不是依赖find

我仍然想知道在这种代码构造中是否存在处理错误的方法,因为从读取其他代码时,这种类型的小/简洁代码在Ruby中被大量使用。

2 个答案:

答案 0 :(得分:2)

您在newestFile功能上或尝试运行File#directory?时是否收到此错误?

如果在newestFile中发生这种情况,您可以执行以下操作:

Find.find(ARGV[0]) do |f| 
  if File.directory?(f)
    newestFile(f) rescue nil
  end
end

这只是忽略了下一个文件夹之前的任何错误和漏洞。如果需要,你也可以做一些更好的输出:

Find.find(ARGV[0]) do |f| 
  if File.directory?(f)
    begin
      newestFile(f)
    rescue
      puts "error accessing: #{f}, you might now have permissions"
    end
  end
end

如果File#directory?中发生错误,您还需要包装该部分:

Find.find(ARGV[0]) do |f|
  begin
    if File.directory?(f)
      newestFile(f)
    end
  rescue
    puts "error accessing: #{f}, you might now have permissions"
  end
end

就像你提到的那样,如果错误发生在Find#find本身,那么你就无法从块中捕获到错误。它必须在该方法内部发生。

您是否可以通过粘贴异常的堆栈跟踪来确认该方法中发生异常而不是后续异常?

修改

我打算建议用Dir#entries之类的东西自己遍历目录,这样你就有了捕获错误的能力。我感兴趣的一件事是你是否在命令行的调用中留下了*。我在MacOS上,所以我不能100%复制你所看到的内容但如果我允许它遍历我无法访问的目录,那么我会打印关于我可以使用哪些文件夹的调试信息&# 39;访问但继续。如果我把它放在另一个上,除了打印第一个文件夹的错误之外似乎什么都不做,它无法访问。

我在MacOS上的一个不同之处在于它实际上并没有抛出异常,它只是将调试信息打印到控制台。但有趣的是,如果我无法访问某个文件夹,那么包含* made会完全停止。

答案 1 :(得分:1)

你可以是被动的或主动的,或者是可行的,但是通过提前测试,你的代码将运行得更快,因为你不会触发异常机制。

不是等待问题发生然后尝试处理异常,而是可以确定您是否应该尝试使用File类owned?尝试更改目录或访问文件, grpowned?方法。来自File文档:

grpowned?(file_name) → true or false
  

如果指定的文件存在且调用进程的有效组ID是文件的所有者,则返回true。在Windows上返回false。

owned?(file_name) → true or false
  

如果指定的文件存在且调用进程的有效使用ID是文件的所有者,则返回true。

这意味着您的代码可能如下所示:

Find.find(ARGV[0]) do |f| 
  if File.directory?(f) && %w[grpowned? owned?].any?{ |m| File.send(m.to_s, f) }
    newestFile(f)
  end
end

Ruby将检查目录条目是否是目录,以及它是由当前进程拥有还是拥有。因为&&是短路的,如果它不是目录,则第二组测试不会被触发。

在某些系统上,如果有大量共享资源,组权限将为您提供更好的访问权限,因此首先进行测试,如果返回true,则any?将返回true并且代码会进步的。如果由于组权限不允许访问而返回false,则owned?将测试该文件,代码将跳过或进入newestFile。根据系统的设置,反转这两个速度测试。或者,使用time ruby /path/to/your/code运行代码,然后旋转两个并再次运行它。比较结果时间,以了解哪个系统更快。

关于使用异常处理来控制程序流是否良好以及不同的语言在编程风格中喜欢不同的东西,有不同的思想流派。对我来说,如果我事先知道我是否可以做某事,而不是尝试让它爆炸,似乎代码将始终运行得更快更安全。如果它以预期的方式爆炸,这是一件事,但如果它以我没想到的方式爆炸,那么我可能没有正确的异常处理,或者它可能触发其他掩盖真正原因的例外情况。我宁愿看看我是否可以通过检查状态来解决问题,然后如果我的所有尝试都失败了,请设置一个允许我优雅退出的异常处理程序。 YMMV。

最后,在Ruby中,我们不使用Camelcase来命名方法,我们使用snake_case。 snake_case_is_easier toReadThanCamelCase。