2012-08-30 35 views
6

Leí algunas líneas de STDIN. ¿Cómo puedo pasar el resto de STDIN a un comando que se lee desde la entrada estándar (por ejemplo, md5sum o wc)?Perl: Pase el manejador de archivo abierto a un programa que lee STDIN

que podía hacer un:

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

pero por razones efficency me gustaría no tocar la entrada, pero en lugar de pasar el gestor de archivo STDIN. Algo así como:

seq 10 | (read A; wc) 

donde read lee tanto como le gusta antes de pasar el resto a wc. Sin embargo, no puedo usar esta solución, ya que necesito iniciar el comando desde dentro de mi programa perl y necesito hacer el trabajo después de que el cmd finalice.


He leído algunas líneas del archivo 'foo'. ¿Cómo puedo pasar el resto a un comando que se lee desde la entrada estándar (por ejemplo, md5sum o wc)?

que podía hacer un:

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

pero por razones efficency me gustaría no tocar la entrada, sino que se pasa el resto del archivo 'foo'.


Tengo la sensación de que puede ser hecho usando trucos como select, open(FOO,">&STDOUT), exec 6<&0, fork, pipe.

Respuesta

8

La respuesta es bastante simple: no necesita hacer nada especial. Su hijo heredará automáticamente su STDIN con system y exec. Todo lo que no haya leído de STDIN será legible por el niño.

Hay un problema, sin embargo. Debido a que leer un carácter a la vez sería una locura ineficiente, Perl lee del archivo un bloque a la vez. Es decir, usted lee más del archivo que las "pocas líneas" que recibió de Perl. Esto se puede ver claramente con el siguiente comando:

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

En lugar de comenzar en el inicio de la segunda línea, cat se inicia en el medio de las s "q" (en bytes 8192)!

Tendría que cambiar de las líneas de lectura con readline (<>) a la lectura de bytes individuales con sysread si desea que esto funcione.


Centrándose en el cuadro más grande, creo que hay una solución:

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 tal vez incluso:

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

No probado.

+0

Solución elegante que le gana el pulgar hacia arriba, pero tiene 2 problemas: Mata a perl (he aclarado la pregunta para dejar en claro que esto no funcionará para mí); y no trata con la segunda parte (leyendo el archivo 'foo'). –

+0

¿Por qué no funciona? Como dice la respuesta, funciona tanto para 'system' como' exec'. Tiene 'perl -e 'print <>; sistema ("cat"); imprimir "todavía aquí \ n" ' tripleee

+0

Bien manchado. Esto funciona: 'seq 10 | perl-e 'sysread (STDIN, $ a, 1); imprimir "$ a bar"; sistema ("cat"); imprimir "todavía aquí \ n" ''. Sin embargo, todavía deja de leer el archivo 'foo'. –

Cuestiones relacionadas