2017-08-13 134 views
4

是否有可能中断正在运行的Ruby脚本,更新它,然后继续运行它?Ruby - 更新运行脚本

E.g.说你有这个脚本:

(0..10).each do |x| 
    puts x 
end 

你可以中断它,修改它,二号线阅读:

puts x * 2 

然后继续执行?

(假设我们忽略琐碎的争论就像中断时间过短)

+0

你看着'load'重装'.rbs'? –

回答

0

pry做一些事情有点像这样。

如果我有像这样的脚本test.rb

require 'pry' 

result = [] 
5.times do |i| 
    result << "#{i}" 
    binding.pry if i == 2 
end 

puts "result is ", result.join(",") 

然后在这是第3次迭代将暂停在断点处,我可以输入命令。如果我进入

edit test.rb 

然后$EDITOR(或纳米)将打开,我可以改变的源代码。当我退出文本编辑器时,pry将处于相同的断点处,但会重新运行现有代码,然后使用修改后的代码。这是一种子外壳,退出它将使原始代码恢复原始断点。退出此第二个断点后,我返回到bash,并且对该文件的编辑已保留。

您也可以传递一个方法名称,但它具有相同的效果(一个子shell),而不是将路径传递给edit。因此,在这两种情况下,与上面的代码,如果我改变

result << "#{i}" 

result << "#{i}*" 

,我会看到无论是result is 0,1,2,3,4result is 0*,1*,2*,3*,4*,但不result is 0,1,2,3*,4*

另外,在pry中,可以在断点处轻松编辑局部变量。例如:

require 'pry' 

a = 1 
binding.pry 
puts a 

如果键入a = 2然后control+d退出断点时,程序将打印2像预期。您还可以使用运行时元编程来覆盖方法,实例变量等。

2

如果你想真正停止的过程中,你可以trap中断信号,写目前的进展到一个文件,然后查找文件开始备份时:

progress_file = './script_progress.txt' 
x = if File.exists?(progress_file) 
    File.read(progress_file).to_i 
else 
    0 
end 

Signal.trap("INT") { 
    File.open(progress_file, 'w') { |f| f.write(x.to_s) } 
    exit 
} 

while x <= 10 do 
    puts x 
    x += 1 

    sleep(1) 
end 

结果:

$ rm script_progress.txt 
$ ruby example.rb 
0 
1 
2 
3 
^C$ cat script_progress.txt 
4 
# modify example.rb here, changing `puts x` to `puts x * 2` 
$ ruby example.rb 
8 
10 
12 
14 
16 
18 
20 

你也可以使用at_exit写入文件脚本退出任何时间(即使它只是正常完成):

progress_file = './script_progress.txt' 
x = if File.exists?(progress_file) 
    File.read(progress_file).to_i 
else 
    0 
end 

at_exit do 
    File.open(progress_file, 'w') { |f| f.write(x.to_s) } 
end 

while x <= 10 do 
    puts x 
    x += 1 

    sleep(1) 
end 

结果:

$ ruby example.rb 
0 
1 
2 
3 
4 
^Cexample.rb:16:in `sleep': Interrupt 
    from example.rb:16:in `<main>' 

# modify example.rb to double the output again 
$ ruby example.rb 
10 
12 
14 
16 
18 
20 

如果你想的过程中继续运行,但为了能够在功能上切换不同的,你可以使用Process.kill发送自定义信号:

pid = fork do 
    Signal.trap("USR1") { 
    $double = !$double 
    } 

    (0..10).each do |x| 
    puts $double ? x * 2 : x 

    sleep(1) 
    end 
end 

Process.detach(pid) 
sleep(5) 
Process.kill("USR1", pid) 
sleep(6) 

结果:

$ ruby example.rb 
0 
1 
2 
3 
4 
10 
12 
14 
16 
18 
20 

你可以用它来告诉ruby到load一个文件再次:

File.open('print_number.rb', 'w') do |file| 
    file.write <<-contents 
def print_number(x) 
    puts x 
end 
contents 
end 

pid = fork do 
    load './print_number.rb' 
    Signal.trap("USR1") { 
    load './print_number.rb' 
    } 

    (0..10).each do |x| 
    print_number(x) 

    sleep(1) 
    end 
end 

Process.detach(pid) 
sleep(5) 
File.open('print_number.rb', 'w') do |file| 
    file.write <<-contents 
def print_number(x) 
    puts x * 2 
end 
contents 
end 
Process.kill("USR1", pid) 
sleep(6) 

结果:

$ ruby example.rb 
0 
1 
2 
3 
4 
10 
12 
14 
16 
18 
20 
+0

有一些疯狂的黑客!红宝石真棒:) –

+1

我也偶然发现了[本演示文稿](http://avdi.org/talks/rockymtnruby-2011/things-you-didnt-know-about-exceptions.html),它展示了一些同样疯狂的想法围绕流量控制 - 例如覆盖'raise'方法,继续执行。 –