【问题标题】:Perl: Pass open file handle to a program reading STDINPerl:将打开的文件句柄传递给读取 STDIN 的程序
【发布时间】:2012-08-30 23:04:31
【问题描述】:

我从 STDIN 读了几行。如何将剩余的 STDIN 传递给从标准输入读取的命令(例如 md5sumwc)?

我可以做一个:

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”中读取了几行。如何将剩余部分传递给从标准输入读取的命令(例如 md5sumwc)?

我可以做一个:

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

但出于效率原因,我不想触摸输入,而是传递文件“foo”的其余部分。


我觉得可以使用 selectopen(FOO,"&gt;&amp;STDOUT)exec 6&lt;&amp;0forkpipe 这样的诡计来完成。

【问题讨论】:

    标签: perl redirect fork filehandle


    【解决方案1】:

    答案很简单:你不需要做任何特别的事情。您的孩子将自动继承您的STDINsystemexec。您尚未从 STDIN 读取的所有内容都将被孩子阅读。

    但是有一个问题。因为一次读取一个字符会非常低效,Perl 一次从文件中读取一个块。也就是说,您从文件中读取的内容比从 Perl 返回的“几行”要多。使用以下命令可以清楚地看到这一点:

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

    cat 不是从第二行的开头开始,而是从“q”中间(字节 8192)开始!

    如果你想让它工作,你必须从使用readline (&lt;&gt;) 读取行切换到使用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);
    ...
    

    未经测试。

    【讨论】:

    • 优雅的解决方案让您赞不绝口,但它有两个问题:它杀死了 perl(我已经澄清了这个问题,以明确这对我不起作用);它不处理第二部分(读取文件'foo')。
    • 为什么不起作用?正如答案所说,它适用于systemexecperl -e 'print &lt;&gt;; system("cat"); print "still here\n"' &lt;file 不代表您的场景的基本情况吗?
    • 很好看。这有效:seq 10 | perl -e 'sysread(STDIN,$a,1); print "$a bar";system("cat"); print "still here\n"'。不过,它仍然会读取文件“foo”。
    • @Ole Tange,错过了这一点,因为我不认为你会想要使用 sysread 的愚蠢解决方案。只需使用open(STDIN, '&lt;', 'foo') or die $!; 或者如果您不想破坏父母的标准输入,open(local *CHILD_STDIN, '&lt;', 'foo') or die $!; waitpid(open3('&lt;&amp;CHILD_STDIN', '&gt;&amp;STDOUT', '&gt;&amp;STDERR', @cmd), 0);
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-10
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    相关资源
    最近更新 更多