2012-08-30 12 views
6

Ho letto poche righe da STDIN. Come posso passare il resto di STDIN a un comando che legge dallo standard input (ad esempio md5sum o wc)?Perl: passa l'handle di file aperti a un programma che legge STDIN

ho potuto fare un:

read_a_few_lines_from_diamond_operator(); 
open (C, "|cmd"); 
while(<>) { print C } 
close C; 
cleanup_after_C(); 

ma per ragioni efficency Vorrei non toccare l'ingresso, ma invece passare il filehandle di STDIN. Un po 'come:

seq 10 | (read A; wc) 

dove read legge tanto quanto lo ama prima di passare il resto a wc. Non posso usare questa soluzione, però, dato che ho bisogno di avviare il comando dal mio programma perl e ho bisogno di fare il lavoro dopo il cmd completo.


Ho letto alcune righe dal file 'foo'. Come posso passare il resto a un comando che legge dallo standard input (ad esempio md5sum o wc)?

ho potuto fare un:

open (F, "<foo"); 
read_a_few_lines_from_F(); 
open (C, "|cmd"); 
while(<F>) { print C } 
close C; 
cleanup_after_C(); 

ma per ragioni efficency Vorrei non toccare l'ingresso, ma invece passare il resto dei file 'pippo'.


Ho la sensazione che può essere fatto utilizzando l'inganno come select, open(FOO,">&STDOUT), exec 6<&0, fork, pipe.

risposta

8

La risposta è abbastanza semplice: non è necessario fare nulla di speciale. Il tuo bambino erediterà automaticamente il tuo STDIN con system e exec. Tutto ciò che non hai letto da STDIN sarà leggibile dal bambino.

C'è un problema, però. Poiché leggere un carattere alla volta sarebbe un pazzo inefficiente, Perl legge dal file un blocco alla volta. Vale a dire che leggi di più dal file rispetto alle "poche righe" che hai ricevuto da Perl. Questo può essere visto chiaramente utilizzando il seguente comando:

perl -E'say $_ x 500 for "a".."z"' \ 
    | perl -e'<>; <>; exec("cat");' \ 
    | less 

Anziché iniziare all'inizio della seconda riga, cat inizia nella metà degli s "Q" (a byte 8192)!

Dovresti passare dalle linee di lettura con readline (<>) alla lettura di singoli byte con sysread se vuoi che funzioni.


Concentrandosi sul quadro generale, penso che ci sia una soluzione:

open(STDIN, "<", "foo") or die $!; 
read_a_few_lines(*STDIN); 
my $pos = tell(STDIN); 
open(STDIN, "<", "foo") or die $!; 
sysseek(STDIN, $pos, SEEK_SET); 
system(@cmd); 
... 

O forse anche:

open(STDIN, "<", "foo") or die $!; 
read_a_few_lines(*STDIN); 
sysseek(STDIN, tell(STDIN), SEEK_SET); 
system(@cmd); 
... 

testato.

+0

Soluzione elegante che ti fa guadagnare un pollice in su, ma ha 2 problemi: uccide perl (ho chiarito la domanda per chiarire che questo non funzionerà per me); e non tratta la seconda parte (leggendo il file 'pippo'). –

+0

Perché non funziona? Come dice la risposta, funziona sia per 'system' che per' exec'. Esiste 'perl -e 'print <>; Sistema ("cat"); stampare "ancora qui \ n" ' tripleee

+0

Ben individuato. Funziona così: 'seq 10 | perl -e 'sysread (STDIN, $ a, 1); stampare "$ a bar"; system ("cat"); stampa "ancora qui \ n" ''. Lascia comunque leggere il file 'foo'. –

Problemi correlati