Perl: передать открытый дескриптор файла программе, считывающей STDIN

я прочитал несколько строк из STDIN. Как я могу передать оставшуюся часть STDIN команде, которая читает из стандартного ввода (например,md5sum или wc)?

я мог бы сделать:

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

но по причинам эффективности я хотел бы не касаться ввода, а вместо этого передать файловую ручку STDIN. Вроде как:

seq 10 | (read A; wc)

здесь read читает столько, сколько ему нравится, прежде чем передать остальное wc. Я не могу использовать это решение, как мне нужно запустите команду из моей программы perl, и мне нужно сделать работу после cmd завершается.


я прочитал несколько строк из файла "foo". Как я могу передать оставшуюся команду команде, которая читает из стандартного ввода (например,md5sum или wc)?

я мог бы сделать:

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

но по причинам эффективности я хотел бы не касаться ввода, а вместо этого передать остальную часть файла "foo".


у меня такое чувство, что это можно сделать используя обман как select, open(FOO,">&STDOUT), exec 6<&0, fork, pipe.

1 ответов


ответ достаточно прост: вам не нужно делать ничего особенного. Ваш ребенок автоматически унаследует ваш STDIN С system и exec. Все, что вы не прочитали из STDIN, будет читаться ребенком.

однако есть проблема. Поскольку чтение одного символа за раз было бы безумно неэффективным, Perl читает из файла блок за раз. То есть Вы читаете больше из файла, чем "несколько строк", которые вы получили от Perl. Это может ясно быть видно, используя следующую команду:

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

вместо того, чтобы начинать в начале второй строки,cat начинается в середине " q " s (в байте 8192)!

вам придется переключиться с чтения строк с readline (<>) для чтения отдельных байтов с sysread если вы хотите, чтобы это сработало.


фокусируясь на более широкой картине, Я думаю, что есть решение:

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

или может даже:

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

непроверенными.