2017-08-13 5 views
4

Ist es möglich, ein laufendes Ruby-Skript zu unterbrechen, zu aktualisieren und dann weiter auszuführen?Ruby - aktuelles laufendes Skript

z. sagen Sie das Skript hatte:

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

Sie es sie unterbrechen konnte, modifizieren, so dass die zweite Zeile zu lesen:

puts x * 2 

dann die Ausführung fortsetzen?

(vorausgesetzt, wir trivial Argumente wie die Unterbrechungszeit zu kurz ignorieren)

+0

Haben Sie sich in 'load' nachladen' .rbs'? –

Antwort

0

pry etwas tut ein bisschen wie diese.

Wenn ich ein Skript test.rb wie so:

require 'pry' 

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

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

Dann am 3. Iteration es an einem Haltepunkt anhalten, wo ich in Befehle eingeben kann. Wenn ich

edit test.rb 

dann $EDITOR (oder nano) eingeben wird geöffnet, und ich kann den Quellcode ändern. Wenn ich den Texteditor verlasse, befindet sich der Schlüssel auf dem gleichen Haltepunkt, aber er wird den vorhandenen Code erneut ausführen, bis er den geänderten Code verwendet. Dies ist eine Art Untershell, und wenn Sie es verlassen, wird der ursprüngliche Breakpoint mit dem ursprünglichen Code zurückgebracht. Nachdem ich diesen zweiten Haltepunkt verlassen habe, komme ich wieder zur bash und meine Änderungen an der Datei wurden beibehalten.

Anstatt einen Pfad an edit zu übergeben, können Sie auch einen Methodennamen übergeben, der jedoch den gleichen Effekt hat (eine Subshell). So in jedem Fall mit dem obigen Code, wenn ich

geändert
result << "#{i}" 

zu

result << "#{i}*" 

, würde ich entweder result is 0,1,2,3,4 oder result is 0*,1*,2*,3*,4*, sieht aber nicht result is 0,1,2,3*,4*.

Darüber hinaus ist es möglich, lokale Variablen leicht in einem Haltepunkt zu bearbeiten. zum Beispiel:

require 'pry' 

a = 1 
binding.pry 
puts a 

Wenn ich a = 2 und dann control+d beenden den Haltepunkt, das Programm druckt 2 wie erwartet ein. Sie können Laufzeit-Metaprogrammierung auch verwenden, um Methoden, Instanzvariablen usw. zu überschreiben.

2

Wenn Sie möchten, um tatsächlich den Prozess zu stoppen, können Sie das Interrupt-Signal trap, schreiben Sie den aktuellen Fortschritt in eine Datei und dann für diese Datei aussehen, wenn wieder nach oben Start:

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 

Ergebnis:

$ 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 

Sie könnten auch at_exit verwenden, um die Datei zu jeder Zeit das Skript beendet zu schreiben (auch wenn es normalerweise gerade beendet):

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 

Ergebnis:

$ 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 

Wenn Sie den Prozess am Laufen halten wollen, aber nur in der Lage sein, funktionell verschiedene wechseln, können Sie ein eigenes Signal mit Process.kill senden:

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) 

Ergebnis:

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

und Sie können, dass ruby ​​zu sagen, verwenden, um eine Datei load wieder:

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) 

Ergebnis:

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

Es gibt einige verrückte Hacks! Ruby ist genial :) –

+1

Ich bin auch über diese Präsentation gestolpert (http://avdi.org/talks/rockymtnruby-2011/things-you-didnt-know-about-exceptions.html), die einige ebenso verrückte Ideen zeigt um die Flusskontrolle - wie zum Beispiel das Überschreiben der 'raise' Methode, um die Ausführung dort fortzusetzen, wo sie aufgehört hat. –

Verwandte Themen