2013-09-01 9 views
6

Der folgende Code druckt nicht die Ausgabe von tail -f. Warum? Wie kann ich es zum Laufen bringen?Back-Tick in Ruby funktioniert nicht mit Tail-F-Befehl

#myApp.rb 
`ls`    #works fine 
`tail -f filename`  #does not work. why? 
+0

Ist ' "Dateiname"' die wörtlichen Namen der Datei (oder ein Dummy-Namen nur für Ihre Frage verwendet wird)? Oder ist es der Name einer Variablen, die eine Zeichenfolge enthält? – sawa

+1

'filename' ist ein Dummy für die Frage – Bhuvan

Antwort

7

Durch die Verwendung der Folge Option -f auf tail den ausgeführten Befehl nicht sofort beenden.

-f, --follow[={name|descriptor}] 
    output appended data as the file grows; 

Die Idee der Verwendung von Backticks (oder die Abkürzung %x), im Gegensatz zu system('...') ist, dass diese Aussagen die Ausgabe der ausgeführten Befehle zurückgeben. Auf diese Weise könnte man das Ergebnis in einer Variablen speichern:

dir_content = `ls` 

tail -f laicht einen anderen Prozess, dieser Prozess hält ohne Abschluss auf die Standardausgabe auf das Schreiben. Daher ist Ihre Aussage nie beendet. Ohne die Anweisung zu beenden, kann ein Wert nicht zurückgegeben werden. Wenn Sie eine wachsende Datei ansehen und ausgeben möchten, lesen Sie die Lösung der folgenden Frage:

Watch/read a growing log file.

Alternativ können Sie den Befehl durch system wie so laufen:

system('tail -f filename') 

Was ist der Unterschied hier? Anstatt den Ausgang des Befehls zurückzugeben, wird der Befehl true (Befehl wird erfolgreich ausgeführt), false (nicht erfolgreich) oder nil (Ausführung des Befehls ist fehlgeschlagen) zurückgegeben. Da die Ausgabe des Befehls nicht an die Return-Anweisung umgeleitet wird, auf der tail -f ausgeführt wird, wird der Inhalt auf die Standardausgabe gedruckt.

Wenn es Ihnen gelingt, die Ergebnisse auf Standardausgabe zu bringen, können Sie sie einfach in einen Thread-Block einfügen. Auf diese Weise wird der wachsende Inhalt von filename in die Standardausgabe geschrieben und Sie können weiterhin anderen Ruby-Code ausführen.

Thread.new { system('tail -f filename') } 

Wenn Sie die vollständige Kontrolle haben, erfassen die Ausgabe, um sie an einer anderen Stelle des Skripts für den Abruf speichern dann auf die Antwort auf die folgende Frage einen Blick haben, die einen solchen Ansatz beschreibt:

Continuously read from STDOUT of external process in Ruby

Es basiert auf dem PTY Modul basiert, die Pseudo-Terminals erstellt und verwaltet. Grundsätzlich können Sie ein anderes Terminal durch dieses Modul spawnen. Der Code könnte dann so aussehen:

require 'pty' 
cmd = "tail -f filename" 
begin 
    PTY.spawn(cmd) do |stdin, stdout, pid| 
    begin 
     # Do something with the output here: just printing to demonstrate it 
     stdin.each { |line| print line } 
    rescue Errno::EIO 
     puts "Errno:EIO error, but this probably just means " + 
      "that the process has finished giving output" 
    end 
    end 
rescue PTY::ChildExited 
    puts "The child process exited!" 
end 

Schließlich gibt es auch einen Socket-Ansatz. Öffnen Sie einen Socket in Bash oder verwenden Sie socat, leiten Sie die Ausgabe von tail -f an den Socket und lesen Sie Ruby vom Socket.

+0

Ihr Beitrag ist ein verwirrtes Chaos und beschreibt, was passiert, anstatt zu erklären, warum es passiert. Der Op weiß bereits, was passiert. Downvoted. Die Personen, die Ihren Beitrag aktualisiert haben, sollten erklären, warum sie den Beitrag geändert haben. Ihre Argumentation lautet etwa so: Wenn Sie einen Befehl ausführen, der keinen Wert zurückgibt, kann er nicht in stdout schreiben; Wenn Sie jedoch die Methode system() von ruby ​​verwenden, um einen Befehl auszuführen, der keinen Wert zurückgibt, wird er in stdout geschrieben. Hä? – 7stud

+0

Ich habe in keiner Art gesagt, dass, wenn Sie einen Befehl ausführen, der keinen Wert zurückgibt, nicht in stdout schreiben kann. Ich sehe auch nicht aus der Frage, was der OP schon weiß und was er nicht weiß. Danke für den Kommentar, ich werde versuchen, die Formulierungen zu verbessern. –

+0

platzhirsch hat geschrieben: _Ohne die Beendigung wird kein Rückgabewert ausgegeben und so wird nichts gedruckt._ Was bedeutet das dann? Oder anders ausgedrückt: Ist der Rückgabewert eines Befehls oder dessen Fehlen relevant dafür, ob ein Befehl in stdout schreiben kann? – 7stud

0

Der Grund, warum es nicht funktioniert, ist, weil die Backticks einen separaten System Aufruf ausführen, um die Befehle innerhalb der Backticks auszuführen. Wenn Sie ps aux | grep tail tun, können Sie sehen, dass der Tail-Prozess tatsächlich ausgeführt wird.

Der Code zeigt keine Ausgabe an, da er darauf wartet, dass der Tail beendet wird und mit den nächsten Anweisungen fortfährt - er hängt nicht, weil er die Ausgabe des Tail-Befehls anzeigt.

Auch im normalen Betrieb müssen Sie eine ctrl+C ausführen, um den Ausgang des Befehls tail -f zu schließen. Die -f wird verwendet, um angehängte Daten auszugeben, wenn die Datei wächst, und daher läuft tail -f weiter. Versuchen Sie, eine kill -9 <pid_of_tail> mit der PID von ps aux | grep tail zu tun. Dann beginnt der verbleibende Teil Ihres Ruby-Codes mit der Ausgabe des Ergebnisses.

Die Anrufe ls, tail without -f funktionieren, weil sie von selbst beendet werden.

Wenn Sie dabei einen Schwanz von Ruby interessiert sind, können Sie einen Blick auf diese file-tail gem http://flori.github.io/file-tail/doc/index.html

1

1) Nach den Backticks docs,

`cmd` 
Returns the standard output of running cmd in a subshell. 

Also, wenn Sie schreiben können:

puts `some command` 

dann sollte Ruby was auch immer einige Befehlsausgaben drucken.

2) Nach dem Schwanz Mann Seiten:

The tail utility displays the contents of file...to the standard 
output. 

Aber der Befehl:

puts `tail -f some_file` 

nichts, trotz der Tatsache, dass das Laufen drucken:

$ tail -f some_file 

sendet Ausgabe nach Standard out. Die Schwanzmann-Seiten sagen auch

The -f option causes tail to not stop when 
end of file is reached, but rather to wait 
for additional data to be appended to the 
[the file]. 

Was bedeutet das? Und wie ist es relevant für das vorliegende Problem?

Da das Programm ausgeführt wird:

puts `tail -f some_file` 

... hängt und nicht ausgegeben etwas tut, kann man daraus schließen, dass die Backticks Methode muss die gesamte Ausgabe des Befehls zu erfassen versuchen. Mit anderen Worten, liest die Backticks-Methode Zeile für Zeile nicht aus der Standardausgabe des Befehls, d. H. Zeilenorientierte Eingabe. Stattdessen liest das Backticks-Verfahren eine Datei-orientierte Eingabe, d. H. Eine Datei zu einer Zeit. Und weil der Befehl tail -f some_file nie beendet, auf Standardausgabe zu schreiben, liest die Methode backticks nie das Ende der Datei, und es hängt, während es auf eof wartet.

3) Allerdings ist das Lesen der Ausgabe eines Befehls eine Datei zu einer Zeit nicht die einzige Möglichkeit, die Ausgabe eines Befehls zu lesen. Sie können die Ausgabe eines Befehls auch Zeile für Zeile mit popen() lesen:

my_prog.rb:

IO.popen('tail -f data.txt') do |pipe| 
    while line = pipe.gets do #gets() reads up to the next newline, then returns 
    puts line 
    end 
end 


--output(terminal window 1):-- 
$ ruby my_prog.rb 
A 
B 
C 
<hangs> 

--output(terminal window 2):-- 
echo 'D' >> data.txt 

--output(terminal window 1):-- 
A 
B 
C 
D 
<hangs> 

Echo Manpages:

The echo utility writes any specified operands...followed 
by a newline (`\n') character. 
+0

funktionierte wie ein Charme Danke :) – Faizan

Verwandte Themen