2009-10-20 14 views
6

Ho uno script Perl che utilizza uno strumento esterno (cleartool) per raccogliere informazioni su un elenco di file. Voglio usare IPC per evitare generando un nuovo processo per ogni file:Come faccio a leggere un IPC non bloccante su Windows?

use IPC::Open2; 
my ($cin, $cout); 
my $child = open2($cout, $cin, 'cleartool'); 

comandi che ritornano singole linee funzionano bene. per esempio.

print $cin "describe -short $file\n"; 
my $description = <$cout>; 

comandi che ritornano più righe mi hanno in un vicolo cieco per il modo di consumare l'intera risposta senza essere appeso da un blocco di leggere:

print $cin "lshistory $file\n"; 
# read and process $cout... 

Ho cercato di impostare il filehandle per non bloccante si legge tramite fcntl:

use Fcntl; 
my $flags = ''; 
fcntl($cout, F_GETFL, $flags); 
$flags |= O_NONBLOCK; 
fcntl($cout, F_SETFL, $flags); 

ma Fcntl muore con il messaggio "il venditore non ha definito Fcntl macro F_GETFL."

Ho provato a utilizzare IO :: Handle per impostare $cout->blocking(0) ma non riesce (restituisce undef e imposta $! su "Errore sconosciuto").

Ho cercato di usare select per determinare se ci sono dati disponibili prima di tentare di leggere:

my $rfd = ''; 
vec($rfd, fileno($cout), 1) = 1; 
while (select($rfd, undef, undef, 0) >= 0) { 
    my $n = read($cout, $buffer, 1024); 
    print "Read $n bytes\n"; 
    # do something with $buffer... 
} 

ma che pende senza mai aver letto nulla. Qualcuno sa come farlo funzionare (su Windows)?

+0

Cosa pensi di fare mentre non blocchi su IO? – jrockway

+0

Voglio leggere fino a quando non interrompo i dati, che sembrerebbero più affidabili del tentativo di determinare che ho raggiunto la fine analizzando i dati che ho già recuperato. (Non c'è nessuna indicazione "questa è l'ultima riga" nei dati.) –

+0

Cercando in giro per un problema correlato, ho trovato questo commit su GitHub, che sembra invocare la magia nera per creare un socket non bloccante: https: // github .com/kthakore/frozen-bubble/commit/735dd2307455e32c69827e013992c2d222cb7347 –

risposta

5

select only works on sockets in Windows. Sembra che IPC :: OpenX utilizzi filehandle normali, quindi non sarà possibile utilizzare select con gli handle creati.

Se non è necessario il timeout/rilevamento di attività che fornisce selezionare, è possibile impostare le maniglie per essere non bloccante e solo leggere o scrivere come al solito.

Se avete bisogno di un maggiore controllo sfumata, IPC::Run può funzionare bene per voi.

Si potrebbe anche guardare a creare un socketpair e utilizzare quelle maniglie con i tuoi processi figlio. I più recenti perls (5.8 e versioni successive) supportano l'emulazione socketpair su Windows tramite socket TCP.

Se si tenta di clonare STDOUT e STDERR per un programma che viene eseguito senza una console (ovvero viene avviato con wperl, anziché perl), non sarà possibile ottenere dati tramite STDIO.

In pratica questo è stato un grande dolore per me in diversi progetti. Quello che ho trovato ha funzionato meglio è stato scrivere il processo figlio per connettersi al server genitore tramite TCP. Se non si controllano i processi figli, consultare IPC::Run o socketpair.

+1

Sì, Windows fa schifo. Quando si esegue lì, IPC :: Run fa il trucco presa che hai menzionato. – ephemient

+0

Come ho scritto nella domanda, non ero in grado di rendere l'handle non bloccante tramite Fcntl o IO :: Handle. Se conosci un modo per farlo (funziona su Windows), condividilo. –

+0

@ Michael: Esegui il blocco degli I/O in un processo separato. Comunicare avanti e indietro con il processo principale utilizzando le prese. Il processo figlio potrebbe bloccarsi, ma il processo principale può eseguire operazioni di I/O non bloccanti sui socket di comunicazione. Questo è ciò che daotoad suggerisce con 'socketpair'. – ephemient

0

L'IO non bloccante è difficile su Windows. In questo caso, è possibile inviare l'output di cleartool ad un file normale, e di utilizzare seek per reimpostare il flag EOF sul file ogni time out leggere dal file:

my($cin, $cout); 
my $somefile = "some/file"; 
open($cin, "| cleartool > $somefile"); 
open($cout, '<', $somefile); 

... 

print $cin "$command_for_cleartool\n"; 
# if necessary, wait until cleartool finishes with new output 
seek $cout, 0, 1;  # clear eof condition from previous read 
my @cleartool_output = <$cout>; # capture recent output 
... process output ... 

Anche se questo non sarà probabilmente il lavoro che bene se cleartool buffer la sua uscita.

1

Un altro kludge è utilizzare sysread con una dimensione del buffer grande o improbabile.

print $cin "$command_for_cleartool\n"; 
    my ($description, $maxlen, $buffer) = ("", 65336); 
    while (my $n = sysread $cout, $buffer, $maxlen) { 
     $description .= $buffer; 
     last if $n < $maxlen; 
    } 
    ... do something with $description ... 

sysread si bloccherà se ci sono esattamente 0 byte di ingresso attesa di essere letti. Quindi il codice sopra si bloccherà se cleartool produce esattamente alcuni multipli di 65336 byte. Se si conosce un buon limite superiore alla dimensione dell'output dal programma, è possibile utilizzare tale valore per $maxlen in precedenza. Altrimenti, potresti scegliere un numero grande e improbabile e pregare ...

Problemi correlati