Ruby 为什么Pathname.rmtree在Signal.trap(';EXIT';)上失败
我正在尝试创建一个临时目录,该目录在脚本退出时被删除Ruby 为什么Pathname.rmtree在Signal.trap(';EXIT';)上失败,ruby,signals,fileutils,pathname,Ruby,Signals,Fileutils,Pathname,我正在尝试创建一个临时目录,该目录在脚本退出时被删除 #/usr/bin/env ruby 需要“路径名” 需要“tmpdir” Tmp_dir=Pathname.new(dir.mktmpdir) 信号陷阱('退出'){ Tmp_dir.rmtree 把“做清理” } puts“将在此消息后退出” Doing cleanup消息永远不会触发,目录保持完整。经过一些测试,似乎Tmp_dir.rmtree从未运行过,也没有任何后续操作 建议Pathname.rmtree调用FileUtils.r
#/usr/bin/env ruby
需要“路径名”
需要“tmpdir”
Tmp_dir=Pathname.new(dir.mktmpdir)
信号陷阱('退出'){
Tmp_dir.rmtree
把“做清理”
}
puts“将在此消息后退出”
Doing cleanup
消息永远不会触发,目录保持完整。经过一些测试,似乎Tmp_dir.rmtree
从未运行过,也没有任何后续操作
建议Pathname.rmtree
调用FileUtils.rm\u r
,因此我尝试这样做:
#/usr/bin/env ruby
需要“fileutils”
需要“路径名”
需要“tmpdir”
Tmp_dir=Pathname.new(dir.mktmpdir)
信号陷阱('退出'){
FileUtils.rm_r(Tmp_dir.to_路径)
把“做清理”
}
puts“将在此消息后退出”
它是有效的。那么为什么rmtree
版本的信号陷阱与内核的At\u出口处理程序不同呢
你在评论中说你不想使用,但实际上并不是一个确切的同义词。实际上,这两种方法的行为明显不同。您可以很容易地看到这一点,如下所示:
# prints nil because variable not in scope; can't raise exceptions
Signal.trap(0) { p defined?(dir); p dir; raise dir }; dir="foo"; exit
# instance variables accessible; won't raise when undefined
Signal.trap(0) { p defined?(@dir); p @dir }; exit
# raises NameError because variable not in scope
at_exit { p defined?(dir); p dir }; dir="foo"; exit
# instance variable in scope, but won't raise when undefined
at_exit { p defined?(@dir); p @dir }; exit
# instance variable in scope; can raise manually
at_exit { p defined?(@dir); p @dir; raise @dir }; exit
如果您想知道它们为什么不同,您可能需要检查解析器或当前Ruby引擎的底层实现。这可能是一个bug,也可能是Ruby核心团队的故意选择。无论哪种方式,它们在Ruby 2.7.1中都有不同的行为
在At_出口处理程序中使用实例变量
正如所演示的,使用带有捕获信号的局部变量会带来范围界定问题,并且所需代码的其他实现可能不可靠,也不符合您的用例。您应该通过注册处理程序,并将临时目录名存储在实例或类变量中
当@dir被定义为顶级作用域中的实例变量或处理程序的已注册实例变量时,通过注册处理程序将实现您对irb或命令行的期望。路径名不是严格必需的,但包含在其中以匹配当前代码
require 'pathname'
require 'tmpdir'
at_exit { @dir.rmtree }
@dir = Pathname.new Dir.mktmpdir
p @dir
使用块进行自动清理
通过使用命令的块形式,可以完全避免信号/退出处理程序中的作用域问题。例如:
require 'tmpdir'
Dir.mktmpdir do |dir|
# do something with dir
end
这将在块退出时清理临时目录,而不必注册退出处理程序或信号陷阱。根据我的经验,这通常比延迟闭包更容易测试和调试,但您的里程数肯定会有所不同。如果这是一个范围问题,
FileUtils
解决方案可能不起作用,但它确实起作用。如果我在退出时使用而不是Signal.trap
(并且不更改代码中的任何其他内容),它也可以工作,但这并不能解释为什么Signal.trap
不能工作,以及为什么rmtree
之后的每个命令都不能运行。块形式不适合我的用例。此外,我想了解代码失败的原因,而不仅仅是解决问题。