开发者

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:

#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.

I've an xml file 'sample.xml'

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

Then I've this perl code

#!/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:

[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.

[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. 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. I think this might require using open3 but I don't know how to use. 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.

You've two problems: your C program is not working and also your Perl program is not working because it goes in a deadlock. 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.

#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:

#!/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. 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. 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.

Can you just add the filename as sample.xml to the $cmd? You could just use the backtick operator to capture the output, chomp removes the newline and the output will be in $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:

#!/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. Try running

cat sample.xml | ./a.out

in shell, and you should get the same output as from your Perl program. 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.

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. The only real guarantee you get about stdin is that you can (try to) read data from it; other than that, it might be anything: a file, a pipe, a tty, etc.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜