2012-09-10 7 views
6

C'è un file:Perl, disabilitare l'ingresso di buffering

:~$ cat fff 
qwerty 
asdf 
qwerty 
zxcvb 

C'è uno script:

:~$ cat 1.pl 
#!/usr/bin/perl 
print <STDIN> 

Il comando come previsto:

:~$ cat fff | perl -e 'system("./1.pl")' 
qwerty 
asdf 
qwerty 
zxcvb 

Ma questo comando non funziona come previsto: il primo <STDIN> legge tutti i dati, non una singola riga. Come disabilitare il buffering per <STDIN>?

:~$ cat fff | perl -e '$_ = <STDIN>; system("./1.pl")' 
:~$ 
+0

sei sicuro 'fff cat. | perl -e 'system ("./ 1.pl") "stampa il contenuto? per me solo 'cat fff | perl 1.pl' fa. – tuxuday

risposta

6

Ci sono due processi Perl qui - la prima che assegna $_ = <STDIN> e chiede system, e il secondo che fa print <STDIN>

Anche se solo la prima riga del flusso viene letto nella $_ dal primo processo, dietro le quinte Perl ha riempito il suo buffer di dati e ha lasciato il flusso vuoto

Qual è lo scopo di questo? L'unico modo che mi viene in mente di fare ciò che si chiede è quello di leggere tutte del file in un array nel primo processo, e quindi rimuovere la prima riga e inviare il resto in un tubo al secondo script

tutto questo sembra inutile, e sono sicuro che ci sia un metodo migliore se si vuole descrivere il problema di fondo

Aggiornamento

Dal momento che dici di essere a conoscenza del problema di buffer, il modo per farlo è utilizzare sysread, che leggerà dalla pipe ad un livello inferiore ed evitare il buffering

Qualcosa di simile a questo lavoro

cat fff | perl -e 'while (sysread(STDIN, $c, 1)) {$_ .= $c; last if $c eq "\n"} system("./1.pl")' 

Ma non mi piace consigliarlo come quello che si sta facendo sembra molto sbagliato e mi auguro che ci si spiegare il tuo vero obiettivo

+0

Puoi leggere una riga, 'tell', riaprire il file e' seek'. – choroba

+0

@choroba: Sì, ma non da una pipe – Borodin

+0

'$ cat fff | perl -ne'print a meno di $. == 1 '| ./1.pl' sarebbe una soluzione poco elegante per rimuovere la prima riga. – amon

0

Recentemente ho avuto per analizzare diversi file di log che erano circa 6 gigabyte ciascuno. Il buffering era un problema dal momento che Perl avrebbe felicemente tentato di leggere quei 6 gigabyte in memoria quando assegnerei lo STDIN a un array ... Tuttavia, semplicemente non avevo le risorse di sistema disponibili per farlo. Ho trovato la seguente soluzione alternativa che legge semplicemente il file riga per riga e, quindi, evita l'enorme vortice di buffering della memoria blackhole che altrimenti richiederebbe tutte le risorse del mio sistema.

nota: tutto ciò che fa questo script è dividere quel file da 6 gigabyte in diversi più piccoli (la cui dimensione è dettata dal numero di righe da contenere in ogni file di output). Il bit interessante è il ciclo while e l'assegnazione di una singola riga dal file di registro alla variabile. Il ciclo eseguirà l'iterazione dell'intero file leggendo una singola riga, facendo qualcosa con esso e quindi ripetendo. Risultato, nessun enorme buffering ... Ho mantenuto intatto l'intero script solo per mostrare un esempio funzionante ...

#!/usr/bin/perl -w 
BEGIN{$ENV{'POSIXLY_CORRECT'} = 1;} 
use v5.14; 
use Getopt::Long qw(:config no_ignore_case); 

my $input = ''; 
my $output = ''; 
my $lines = 0; 
GetOptions('i=s' => \$input, 'o=s' => \$output, 'l=i' => \$lines); 

open FI, '<', $input; 

my $count = 0; 
my $count_file = 1; 
while($count < $lines){ 
    my $line = <FI>; #assign a single line of input to a variable 
    last unless defined($line); 
    open FO, '>>', "$output\_$count_file\.log"; 
    print FO $line; 
    $count++; 
    if($count == $lines){ 
     $count=0; 
     $count_file++; 
    } 
} 
print " done\n"; 

script viene richiamato sulla linea di comando come:

(il nome dello script) -i (file di input) -o (file di output) -l (dimensione del file di output (cioè il numero di linee)

Anche se non è esattamente quello che stai cercando, spero che vi darà alcune idee :)

Problemi correlati