dmor4477 has asked for the wisdom of the Perl Monks concerning the following question:

Hi everybody,

I'm testing a simple IPC implementation that uses Linux pipes to communicate a producer script and a consumer. The producer script looks like:

while(1){ print "Hello\n"; sleep(2); }

And the consumer:

my @lines; while(1){ sleep(10); if (<STDIN>){#Non-blocking reading print "Reading lines..."; @lines=<STDIN>; print "@lines"; }else{ print "No lines received.\n"; } }

If I run the producer or the consumer alone, there are no problems.

$./producer.pl Hello Hello $echo "Hello" | ./consumer.pl Hello

But if I run them together, then de producer does nothing.

$./producer.pl | ./consumer.pl

How can I connect the producer standar output and consumer standar input?

Thanks in advance,

David,

Replies are listed 'Best First'.
Re: Read from a Linux pipe hangs.
by JediWizard (Deacon) on Feb 23, 2005 at 16:14 UTC

    By my understanding reading from STDIN (or any file handle) in array context will read everything untill an end of file character is found. That means that the "consumer" program will wait on the @lines = <STDIN>; line until the producer program from which you are piping information finishes running (which producer.pl never does). I believe you can get arround this probelm using IO::Select. Something like the following:

    while (1) { # This tells us if the processReader has data for us to read if(@ready=$select->can_read(0)) { # Read the data, if it fails, its because we reached # the End of file. if(read($ready[0], $tmp, 128)) { $read_flag=1; } # Add the output from the process to the variable $output.="$tmp"; # We don't have any more to read, break out of the loop if(eof($ready[0]) ) { last; } }else{ sleep(2); } }
    May the Force be with you
      I tried usinf IO::Select adding STDIN: 
      $select = IO::Select->new();
      $select->add(\*STDIN);
      
      But the 'if(@ready=$select->can_read(0))' is always false.
      Is there any methos to read from a FILE_HANDLER line by line instead of reading till the end of file?
      

        Is there any methods to read from a FILE_HANDLER line by line instead of reading till the end of file?

        To read from a filehandle a single line at a time, simply do so in scalar context.

        my $line = <STDIN>;

        will read a single line from STDIN. Note: "Line" is defined based on the value in $/. This defaults to "newline" which is defined based on the operating system perl is running under. If you wish to controll the character which is used to determine what is the end of a "line" you can set $/ (however this should be done in limited scope, probably via local).

        May the Force be with you
Re: Read from a Linux pipe hangs.
by Ultra (Hermit) on Feb 23, 2005 at 19:20 UTC
    Hello.
    First of all using the diamond operator (<>) is *not* non-blocking.
    Also, using print means that you use buffers; the problem is not in the consumer, but in the producer.
    See the following working examples:
    #!/usr/bin/perl -w # producer.pl use strict; while(1){ syswrite(STDOUT,"Hello\n",length("Hello\n")); sleep(2); }

    #!/usr/bin/perl -w # consumer.pl use strict; my $line; while ($line = <STDIN>){ print "$line"; } =pod my $line; my $buf; while (sysread(STDIN,$buf,1)){ if ($buf =~ /\n|\r|\r\n/o){ print "$line\n"; $line = ""; }else{ $line.=$buf; } } =cut
    Hope this helps ;-)
    Dodge This!
      
      Yes they work very well. Now there's the second part of the problem:
      
      I need the read non-blocking of check if there's any
      data ready before doing the 'sysread()'.
      
      The complete consumer is a program that reads account
      packets from a radius server (this problem has been solved)
      and stores the Radius attributes (MAC,IP,User_name) in a 
      List of Hashes. This list of hashes has to be processed 
      every 60 seconds.
      
      The consumer only reads from stdin in order to add new
      packets to the listofHashes.
      
      
      Here there's the consumer that works with:
      #cat accounts| ./consumer.pl
      where accounts has:
      MAC1;IP1;USer1
      MAC2;IP2;USer2
      
      
      #consumer.pl
      #!/usr/bin/perl -w
                                                                                                                                  
      my (%account,@accountList,@accountListTemp);
                                                                                                                                  
      while(1){
          my (@lines,$line,@values);
          sleep(60);
          @lines = <STDIN>;
          foreach $line (@lines){
              #MAC;IP;User_Name\n
              chomp($line);
              @values = split ';',$line;
              $account{"MAC"} = $values[[0]];
              $account{"IP"} = $values[1];
              $account{"Name"} = $values[2];
              push @accountList, {%account};
          }
                                                                                                                                  
          my $i;
          for $i (0 .. $#accountList){
                  #Process @accountList <-- IMPORTANT !!                                                                                                                            
          }
      }
      
        You can read non blocking using select, and looping trough select until there is some data to read.
        Reading using the diamond operator or sysread is blocking. The difference between the two is that using sysread you can control how much data to read at once.
        Also, i see you are using cat some_file|consumer.pl, so i don't understand why you are looping and sleeping in consumer, as cat happens to give you data only once ;-) (if you "cat" a closed file)
        I also think that you are using the wrong approach here. If you want the consumer to read data at certain amounts of time, a better approach IMHO is to do blocking reads in a while(1) without sleeping, and feeding the consumer at the needed intervals. I.E:
        shell> for ((;;)); do cat /usr/share/dict/words;sleep 10 ;done |./cons +umer.pl
        Note that by not running cat just once, you will not receive end-of-file, and as such you need to read using sysread from the consumer.
        Dodge This!