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

时间:2011-10-03 12:58:30

标签: c xml perl

这里可能有很多关于相同的问题,但到目前为止我都没有。

我有一个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;
}

我将其编译为输出a.out。

我有一个xml文件'sample.xml'

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

然后我有这个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;

如果我运行通过stdin传递simple.xml的C程序,我得到以下内容:

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

当我运行perl代码时,我希望大小为60.但我得到0。

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

那么我该如何解决这个问题呢?我需要从perl中的@array传递sample.xml。并且C程序中的stderr整数应该存储在一个单独的变量中,而来自C程序的stdout应该存储在perl中的另一个单独变量中。我认为这可能需要使用open3但我不知道如何使用。任何工作实例都将受到高度赞赏。

2 个答案:

答案 0 :(得分:4)

更新:至于您的评论,Ilmari Karonen已经向您解释过,管道没有文件大小,因为它是一个数据流,程序不知道这个流有多大。

你有两个问题:你的C程序不能正常工作,你的Perl程序也没有工作,因为它陷入了僵局。你不能一起测试这两件事。将这两个问题分开。例如,首先使用shell中的管道尝试您的程序。

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

最初它没有用。为了使它工作,我使用了这个修改过的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;
}

至于你的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;

在我开始阅读之前,你可以看到我关闭了编写器,因为我的C程序旨在获取所有输入,然后进行输出。现在,整个进程间通信将起作用:

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

作为附注,您不需要关闭$ pid,因为它只是表示子进程ID的数字。在其他情况下,您可能想要探索non blocking reads,但会使逻辑变得更复杂。

原始答案:没有解决海报问题,因为他想使用IPC。

您可以将文件名作为sample.xml添加到$ cmd吗?你可以使用反引号运算符来捕获输出,chomp删除换行符,输出将是$ out。

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

我想你的例子是找到一种在C和Perl之间进行通信的方法,因为当然,如果只是文件大小,它在Perl中会更容易:

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

答案 1 :(得分:2)

我很确定问题不在您的Perl代码中,而是在C程序中。尝试运行

cat sample.xml | ./a.out
在shell中

,你应该得到与你的Perl程序相同的输出。问题是,在这两种情况下,stdin将是管道而不是文件,fstat()显然不能为管道返回任何有意义的大小。

无论如何,在fstat()上调用stdin对我来说似乎是不好的做法 - 你不应该依赖于本质上由shell做出的优化。关于stdin的唯一真正保证是你可以(尝试)从中读取数据;除此之外,它可能是任何东西:文件,管道,tty等。