Perl: pass open file descriptor to STDIN reader

I read a few lines from STDIN. How do I pass the rest of the STDIN to a command that reads from standard input (for example, md5sum or wc )?

I could do:

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

but for reasons of efficiency, I would like not to touch the input, but instead pass the STDIN file descriptor. Example:

 seq 10 | (read A; wc) 

where read reads as much as he likes before transferring the rest to wc . However, I cannot use this solution, since I need to run the command from my perl program, and I need to do the work after cmd completes.


I read a few lines from the file 'foo'. How do I pass the rest of the command to a command that reads from standard input (for example, md5sum or wc )?

I could do:

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

but for reasons of efficiency, I would like not to touch the input, but instead passed the rest of the file 'foo'.


I have a feeling that this can be done using tricks like select , open(FOO,">&STDOUT) , exec 6<&0 , fork , pipe .

+6
source share
1 answer

The answer is quite simple: you do not need to do anything special. Your child will automatically inherit your STDIN using system and exec . Everything that you have not read from STDIN will be readable by the child.

However, there is a problem. Since reading one character at a time would be insanely inefficient, Perl reads a block from a file at a time. That is, you read more from the file than the "few lines" that you returned with Perl. This can be seen with the following command:

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

Instead of starting at the beginning of the second line, cat starts in the middle of "q" s (in byte 8192)!

You will need to switch from reading lines with readline ( <> ) to reading individual bytes with sysread if you want this to work.


Focusing on the bigger picture, I think there is a solution:

 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); ... 

Or maybe even:

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

Unverified.

+8
source

Source: https://habr.com/ru/post/924222/


All Articles