简体   繁体   English

Perl将数据传递到stdin并从stdout和stderr读取

[英]Perl passing data to stdin and reading from stdout and stderr

There might be many questions available here regarding the same, but none works for me as of now. 此处可能有许多关于同一问题的问题,但到目前为止,对我来说,这些问题都不起作用。

I've a C program as follows: 我有一个C程序,如下所示:

#include <stdio.h>
#include <sys/stat.h>

long size(FILE *st_in) {
    struct stat st;
    if (fstat(fileno(st_in), &st) == 0)
        return st.st_size;
    return -1;
}

int main (){
   FILE *file = stdin;
   long s1, s2; 

   s1 = size(file);
   printf("Size is %ld\n", s1);
   fprintf(stderr, "%d", 1);
   return 0;
}

I compile it as an output a.out. 我将其编译为输出a.out。

I've an xml file 'sample.xml' 我有一个xml文件“ sample.xml”

<sample>
    <tag>Hello</tag>
    <tag>Cool</tag>
</sample>

Then I've this perl code 然后我有这个Perl代码

#!/usr/bin/perl
use warnings;
use IPC::Open2;
open FILE, "sample.xml" or die $!;
my @xmlfile = <FILE>;
my $pid = open2(*Reader, *Writer, "./a.out");
print Writer "@xmlfile";
waitpid($pid, 0);
my @got = <Reader>;
close Writer;
close Reader;
close $pid;
print "Output got is\n";
print @got;

If I run the C program passing simple.xml via stdin, I get the following: 如果运行通过stdin传递simple.xml的C程序,则会得到以下信息:

[bharath@xml 18:22:34]$ ./a.out < sample.xml 
Size is 60

When I run the perl code, I expect the size as 60. But I get as 0. 当我运行perl代码时,我希望其大小为60。但是我的值为0。

[bharath@xml 18:22:42]$ ./firmware.pl 
Output got is
Size is 0

So how do I solve this? 那么我该如何解决呢? I need to pass the sample.xml from an @array in perl. 我需要从perl中的@array传递sample.xml。 And the stderr integer from the C program should get stored in a separate variable and the stdout from the C program should get stored in another separate variable in perl. 来自C程序的stderr整数应存储在单独的变量中,而来自C程序的stdout应存储在perl中的另一个独立变量中。 I think this might require using open3 but I don't know how to use. 我认为这可能需要使用open3,但我不知道如何使用。 Any working example would be highly appreciated. 任何工作示例将不胜感激。

UPDATE : as for your comments Ilmari Karonen already explained you that a pipe doesn't have a filesize because it's a stream of data, the program doesn't know how big this stream could be. 更新 :关于您的评论,Ilmari Karonen已经向您解释了管道没有文件大小,因为它是数据流,程序不知道该流有多大。

You've two problems: your C program is not working and also your Perl program is not working because it goes in a deadlock. 您有两个问题:C程序无法正常工作,Perl程序也无法正常工作,因为它陷入了僵局。 You can't test the two things together. 您无法同时测试这两件事。 Separate the two problems. 将两个问题分开。 For instance first try your program with the pipe from the shell. 例如,首先使用外壳程序中的管道尝试程序。

cat sample.xml | ./a.out 
Size is 60

Originally it wasn't working. 本来它不起作用。 To make it work I've used this modified C program: the size could be calculated from the stream by counting all received chars until EOF. 为了使它起作用,我使用了这个经过修改的C程序:可以通过计算所有接收到的字符直到EOF来从流中计算大小。

#include <stdio.h>

int main (){
    long size = 0;
    int ch;
    FILE *file = stdin;
    if (!file) {
        return 2;
    }
    while ((ch = getc(file)) != EOF) {
        ++size;
    }
    printf("Size is %ld\n", size);
    fprintf(stderr, "%d", 1);
    return 0;
}

As for your Perl program you had a deadlock because both programs were in wait state, to solve it I've changed slightly the order of the instructions: 至于您的Perl程序,由于两个程序都处于等待状态,因此您陷入了僵局,为解决这个问题,我对指令的顺序做了一些更改:

#!/usr/bin/perl -w

use strict;
use IPC::Open2;

open FILE, "sample.xml" or die $!;
my @xmlfile = <FILE>;

my $pid = open2(*Reader, *Writer, './a.out 2> /dev/null');
print Writer @xmlfile;
close(Writer);

my @got = <Reader>;
close(Reader);
waitpid($pid, 0);

print "Output got is: ";
print @got;

As you can see I close the writer, before I start to read because my C program is designed to get all the input and then do the output. 如您所见,在开始阅读之前,我关闭了编写器,因为我的C程序旨在获取所有输入,然后执行输出。 And now the whole interprocess communication will work: 现在,整个进程间通信将起作用:

./program.pl
Output got is: Size is 60

As a side note you don't need to close $pid, since is just a number representing the process id of the child. 附带说明一下,您不需要关闭$ pid,因为它只是表示子进程ID的数字。 In other cases you might want to explore non blocking reads , but will make the logic more complicated. 在其他情况下,您可能希望探索非阻塞读取 ,但会使逻辑更加复杂。

ORIGINAL ANSWER : didn't solve the poster problem because he wanted to use IPC. 原始答复 :未能解决张贴者问题,因为他想使用IPC。

Can you just add the filename as sample.xml to the $cmd? 您可以仅将文件名作为sample.xml添加到$ cmd吗? You could just use the backtick operator to capture the output, chomp removes the newline and the output will be in $out. 您可以只使用反引号运算符捕获输出,chomp删除换行符,输出将在$ out中。

#!/usr/bin/perl
$cmd = './a.out < sample.xml 2> /dev/null';
$out = `$cmd`;
chomp $out;
print "out='$out'\n";

I guess that your example is to find a way to communicate between C and Perl, because of course, if it's just for the file size it's much easier in Perl: 我想您的示例是找到一种在C和Perl之间进行通信的方法,因为,当然,如果只是为了文件大小,那么在Perl中就容易得多:

#!/usr/bin/perl
print -s 'sample.xml';
print "\n";

I'm pretty sure the problem is not in your Perl code, but in the C program. 我很确定问题不在您的Perl代码中,而是在C程序中。 Try running 尝试跑步

cat sample.xml | ./a.out

in shell, and you should get the same output as from your Perl program. 在shell中,您应该获得与Perl程序相同的输出。 The problem is that, in both cases, stdin will be a pipe instead of a file, and fstat() obviously can't return any meaningful size for a pipe. 问题在于,在两种情况下, stdin都是管道而不是文件,并且fstat()显然无法为管道返回任何有意义的大小。

In any case, calling fstat() on stdin seems like bad practice to me — you shouldn't be relying on what is essentially an optimization made by the shell. 无论如何,在stdin上调用fstat()对我来说似乎是一种不好的做法-您不应该依赖于本质上由Shell进行的优化。 The only real guarantee you get about stdin is that you can (try to) read data from it; 关于stdin的唯一真正保证是您可以(尝试)从stdin中读取数据。 other than that, it might be anything: a file, a pipe, a tty, etc. 除此之外,它可能是任何东西:文件,管道,tty等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM