Link to home
Start Free TrialLog in
Avatar of ServerOverflow
ServerOverflow

asked on

parsing input from endless input?

Im making a perl program that is running as a subprogram of something else,
the stdin and stdout for this perl program are directed towards the perl's parent program. So stdin/out is actually coming from the parent.

The problem is is that I need to get a bunch of lines of input and then process them, but the way the system is set up, I can't do "@all = <STDIN>" because the stdin never has an EOF signal, if I DO do it, it will just wait for ever thinking more input will be coming...

Is there a way to read in until the program isn't being passed anything more, that is, check that stdin isn't returning anything more.

I would like to know because, the input that comes in could vary in length, so I can't just read in 'X' times and stop.


Thanks
Avatar of ServerOverflow
ServerOverflow

ASKER

Perhaps is there a way to read stdin and then if nothing happens after (say 5 seconds) stop reading?
Avatar of wilcoxon
You should be able to use $SIG{ALARM} to break out of reading from <IN>.  You should be able to find more info in perldoc (or on perldoc.com) or in Programming Perl.
ASKER CERTIFIED SOLUTION
Avatar of blinkie23
blinkie23

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hmmm ok, blinkie that stops just like you said, very nice, but how can I get it to just exit the loop and continue on with the rest of the code?

right now it exits the program and I've tried changing the SIGALRM line to last, but no matter what it just exits... very annoying...


Thanks...
that is annoying... but if i put code after my while statement, it executes it normally for either alarm signal that is caught.  i'm not sure why it works for me, and not you.  i am on a unix platform, maybe you're on a windows one?  ... can you troubleshoot the problem out more?  confirm that both the breaks exit the program, or just one of the breaks cause an exit.

Perhaps this:

  #! /usr/bin/perl

  my @processed_data;
  my @unprocessed_data;

  $SIG{ ALRM } = sub {
    process_data();
    alarm( 2 );
  };

  alarm( 2 );
  while( <STDIN> ) {
    push @unprocessed_data, $_;
  }

  sub process_data {
    @processed_data = (); # Remove this line if you don't want incremental processing.
    foreach (@unprocessed_data) {
      my $processed_data = "> $_";
      push @processed_data, $processed_data;
    }
    @unprocessed_data = ();

    # ===== DO SOMETHING WITH PROCESSED DATA
    open FILE, ">>processed.txt";
    print FILE @processed_data;
    close FILE;
  }

What this does:

The alarm is set just before the program gets into the while loop and starts reading data and sending it to a stack (@unprocessed_data), which holds the data for processing. When the alarm goes off, the program's flow of control switches to the alarm handler, which processes the data, and transfers the processed data to another stack. It also empties the unprocessed data stack. The neat part is that the signal handler resets the alarm, so it will happen in timely increments.

To do what you mentioned that you wanted to do, you might try to set a flag:

  #! /usr/bin/perl

  my $exit = undef;
  $SIG{ ALRM } = sub { $exit = 1 };

  alarm( 5 );
  while( <STDIN> && ! $exit ) {
    print;
  }

It may be that the last() is confused about what scoping it's in. Labels may fix this, but flags are, IMHO, more readable.

  #! /usr/bin/perl

  $SIG{ ALRM } = sub { last INPUT_LOOP; }

  INPUT_LOOP: while( <STDIN> ) {
    print;
  }

Hope this helps,

- m.
You could also try using eval to do the equivalent of a try/catch:


eval {
    alarm(3);
    $SIG{ALRM} = sub { die "timeout\n" };

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

if ($@ =~ /timeout/) {
    print "Timed out!\n";
} else {
    die $@;
}

Sorry .. should have been this:

$|++;

eval {
    alarm(3);
    $SIG{ALRM} = sub { die "timeout\n" };

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

if ($@ =~ /timeout/) {
    print "Timed out!\n";
} elsif (defined $@) {
    die $@;
}
The solutions posted above are pretty good.  Just as a dumb question: any reason why the parent program can't pass in some indicator that it's done giving data?  That would be a bit cleaner than waiting a certain number of seconds, I think.

Just an idea,
Tats
Use next instead of die.
if ($@ =~ /timeout/) {
   print "Timed out!\n";
} elsif (defined $@) {
   next "";
}


~Kelly W. Black
Ok thanks guys, all of them were good,
blinkie was the first to answer and that worked after putting the whole thing inside an eval{} expression so im giving it to him ;)


btw. I didnt make the parent program and I dont have source code so I can't change the way it sends its children messages, pretty lame I know, gotta love programmers that don't think ahead...


Thanks to EVERYONE!