【问题标题】:Perl passing data to stdin and reading from stdout and stderrPerl 将数据传递到标准输入并从标准输出和标准错误中读取
【发布时间】:2011-10-03 12:58:30
【问题描述】:

这里可能有很多关于相同问题的问题,但目前没有一个对我有用。

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

如果我运行通过标准输入传递 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 但我不知道如何使用。任何工作示例都将受到高度赞赏。

【问题讨论】:

  • waitpid 之前关闭Writer 是否有帮助?这在这里似乎有所帮助,尽管我无法精确复制(也没有尝试太精确)。

标签: c xml perl


【解决方案1】:

更新:至于您的 cmets,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";

【讨论】:

  • 好吧,目的不是这样。只是这是一个更大问题的剥离版本。我有某些需要通过 perl 调用的 C 程序,我需要将 xml 'data' 传递给 C 程序。 xml 数据是在 perl 中的一个数组中获取的。为了证明这一点,我从这里的文件中读取它。
  • 知道了,解决了 IPC 通信问题,您在 C 和 Perl 部分都遇到了问题。
  • 感谢您的努力@stivlo。我需要在读取 xml 之前获取大小,因为我会使用它来 malloc 一个该大小的数组并将 xml 内容放入其中。所以如果我使用 getc,我会丢失数据。 xml 内容将从动态生成的数组中进一步处理。
  • 然后从 Perl 传递它,因为它是唯一可以知道大小的。您可以将其作为第一行传递到流中。
  • 谢谢!我选择了这个作为最合适的答案! :-)
【解决方案2】:

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

cat sample.xml | ./a.out

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

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

【讨论】:

  • 是的,你是对的:$ ./a.out
  • 嗨@limari Karonen。你的建议是一个很好的观点。如上所述,我有某些需要通过 perl 调用的 C 程序,并且我需要将 xml 'data' 传递给 C 程序。 xml 数据是在 perl 中的一个数组中获取的。传输数据的最佳方法是什么? C程序应该如何获取输入?数据的大小是可变的。我还需要在 perl 中的单独变量中获取 stdout、stderr 的值。
  • @Bharathwaaj:您的 XML 解析器真的需要单个字符串中的输入吗?我见过的大多数人都乐于从输入流中分块读取 XML。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 2014-07-22
  • 1970-01-01
  • 2017-08-20
  • 1970-01-01
  • 2011-09-14
  • 2011-03-24
相关资源
最近更新 更多