I read a few lines from STDIN. How can I pass the remaining of STDIN to a command that reads from standard input (e.g. md5sum
or wc
)?
I could do a:
read_a_few_lines_from_diamond_operator();
open (C, "|cmd");
while(<>) { print C }
close C;
cleanup_after_C();
but for efficency reasons I would like not to touch the input, but instead pass the filehandle of STDIN. Sort of like:
seq 10 | (read A; wc)
where read
reads as much as it likes before passing the rest on to wc
. I cannot use this solution, though, as I need to start the command from inside my perl program and I need to do work after cmd
completes.
I read a few lines from the file 'foo'. How can I pass the remaining to a command that reads from standard input (e.g. md5sum
or wc
)?
I could do a:
open (F, "<foo");
read_a_few_lines_from_F();
open (C, "|cmd");
while(<F>) { print C }
close C;
cleanup_after_C();
but for efficency reasons I would like not to touch the input, but instead pass the rest of file 'foo'.
I have a feeling it can to be done using trickery like select
, open(FOO,">&STDOUT)
, exec 6<&0
, fork
, pipe
.
The answer is quite simple: You don't need to do anything special. Your child will automatically inherit your STDIN
with system
and exec
. Everything you haven't read from STDIN will be readable by the child.
There is a problem, though. Because reading one character at a time would be crazy inefficient, Perl reads from the file a block at a time. That is to say you read more from the file than the "few lines" you got back from Perl. This can clearly be seen using the following command:
perl -E'say $_ x 500 for "a".."z"' \
| perl -e'<>; <>; exec("cat");' \
| less
Instead of starting at the start of the second line, cat
starts in the middle of the "q"s (at byte 8192)!
You'd have to switch from reading lines with readline
(<>
) to reading individual bytes with sysread
if you wanted 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);
...
Untested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With