Thread Frage zu sysread
(18 answers)
Opened by bianca at 2013-10-01 09:18
Nun habe ich mich deutlich intensiver damit auseinandergesetzt, als ich eigentlich vorhatte :-)
Für mich ist die eindeutige Quintessenz: IPC::Open3 sollte man unter Windows nicht verwenden. Zumindest nicht mit Filehandles. Um sysread vernünftig einzusetzen brauchst Du auch select() und das funktioniert unter Windows bekanntlich nur für Sockets. Du könntest also auf Sockets umbauen: http://www.perlmonks.org/?node_id=811081 Gibt aber wohl auch viele Programme, deren Ausgabe sich ungern in Sockets umlenken lässt Das einfachste aber uneleganteste wäre ein Workaround mit sinnlosen Schleifendurchläufen: Code (perl): (dl
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #!/usr/bin/perl use strict; use warnings; use IPC::Open3; use IO::Handle; STDOUT->autoflush(1); my $prog = join(' ',qw(perl doit.pl)); print "Rufe Programm '$prog' auf \n"; my ($in,$out,$err,$pid,$ausgabe,$buffer); eval { $pid = open3($in,$out,$err,$prog); $out->blocking(0); }; die "Gescheitert: '$@'" if $@; if (defined $pid) { print "PID '$pid', lese...\n"; for( my $i = 1; $i <= 100000; $i++ ) { undef $buffer; #select(undef, undef, undef, 0.25) # Verzoegerung my $bytes = sysread($out, $buffer, 4096); $ausgabe .= $buffer if $buffer; #last if $ausgabe && ! $bytes; # Abbruch sobald einmal nichts mehr gelesen wurde } print "Fertig mit lesen\n"; if (defined $ausgabe) { print "\n\nErhalte:\n".('=' x 30)."\n$ausgabe"; my $code = <STDIN>; chomp($code); print $in $code."\n"; sysread $out,$ausgabe,4096; print "\n\nErhalte:\n".('=' x 30)."\n$ausgabe"; } } else { die "Keine PID"; } Die Anzahl der notwendigen Schleifendurchläufe ist vermutlich vom System abhängig, wäre also anzupassen. Man könnte auch die Schleifenzahl erhöhen und das last einkommentieren. Sobald die Ausgabe beginnt, muss aber am Stück gelesen werden können, sonst ist der Zug abgefahren. Etwas robuster ginge es, indem man einen Sinnlos select() zur Verzögerung verwendet. Vor jedem read 250ms Gedenkzeit sollte dicke reichen. Wenn die Anwendung zu Beginn länger braucht, weil sie erst initialisiert, dann ist das über die Schleife abgefangen. Das ganze wird durch zwei Probleme bei sysread() notwendig. 1. Wenn Du sysread aufrufst, obwohl das Handle noch nicht fertig ist, wartet es ewig und ignoriert auch, wenn irgendwann was kommt. 2. Wenn Du blocking abstellst, dann läuft sysread zwar durch, aber Du kannst nicht feststellen, nach dem wievielten Durchlauf es fertig ist. $bytes ist immer undef, wenn es nichts gelesen hat. Egal ob das Handle noch nicht fertig ist, ob die Ausgabe beendet ist oder ob nur gerade nichts kommt. Ich weiß auch nicht sicher, ob es das überhaupt feststellen kann. Robust ist mein Vorschlag leider trotzdem nicht. Baue in Dein doit.pl nach der ersten print() Anweisung ein sleep(2) und eine weitere print() Anweisung ein. Die zweite Ausgabe wirst Du erst nach Deiner Eingabe sehen. So ein ähnliches Problem habe/hatte ich mit den Net::SSH Modulen auch. Dort habe ich mir so beholfen, dass ich entscheide, wann die letzte Zeile erreicht ist (weil ich weiß, was in der letzten Zeile stehen muss). Bei Dir könnte das "Zugangscode eingeben" sein o.ä.. Sobald Du Dich auf einer Shell bewegst kannst Du die letzte Zeile immer selbst erzeugen, indem Du nach jedem Kommando ein NOP() oder echo ausführst und bis zu dessen Ausgabe liest. Es gibt noch einige andere Möglichkeiten - ich würde Dir empfehlen, mal IPC::Run zu testen. Das ist etwas Higher-Level als IPC::Open und bringt wohl trotz einer ganzen Latte Win32-Limitations auch auf Windows sehr gute Ergebnisse. GRuß MArtin Last edited: 2013-10-02 14:42:17 +0200 (CEST) |