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

Hi,

My perl script executes a command on unix box and saves the output to a filehandler. The command it executes prints lines of stats every 10 seconds to standard out.

The command the perl script executes will always run and if the program it is trying to get the stats for isn't available then it just waits until the program is back up again. What I want to do is print the text "No data" if there are no more new lines after 10 seconds?

This is the code so far:
open(STATISTICS, "/home/<app>/client/bin/<stats> -c usage_counter 2> +/dev/null |") || die "can't fork: $!"; while (<STATISTICS>) { print $_; } close STATISTICS || die "ERROR : $! $?";

Replies are listed 'Best First'.
Re: Check for a new line
by kcott (Archbishop) on Jan 28, 2014 at 01:26 UTC

    G'day h123,

    Could you not simply use alarm? Here's a barebones example:

    Perl script:

    #!/usr/bin/env perl use strict; use warnings; use autodie qw{:all}; my $command = './pm_test_no_echo_nl.sh'; my $timeout = 3; my $pid = open my $pipe_from, '-|', $command; local $SIG{ALRM} = sub { kill HUP => $pid; die "No data\n" }; alarm $timeout; while (<$pipe_from>) { alarm 0; print; alarm $timeout; }

    Shell script:

    #!/bin/sh echo With newline echo With newline echo "NO newline\c" sleep 5 echo With newline

    Output from running the shell script from the command line:

    With newline With newline NO newlineWith newline

    [When the last line is output, "NO newline" appears immediately, then there's a 5 second delay, then "With newline" appears and the script ends.]

    Output from the Perl script:

    With newline With newline No data

    [The first two lines (containing "With newline") appear immediately, then there's a 3 second delay, then "No data" appears and the script ends immediately.]

    You should probably also read the documentation for "/home/<app>/client/bin/<stats>" to determine what the appropriate signal is (for kill) to terminate this program in case it's hanging rather than dying (kill 0 => $pid should tell you that): HUP was fine for my shell script; you may need something different.

    See also open (for differences between my syntax and yours) and autodie.

    -- Ken

      Hi Ken, This is the thing I am looking for. Thanks you so much.
      Is there a way to print "No data" every x seconds until program 'B' comes back up? I don't want to kill the process. I'd rather log No data every 10 seconds. If this makes sense?

        You control the timeout activity with the coderef you assign to $SIG{ALRM}. There's no requirement to kill or die.

        In the test shell script, I increased the delay (to get a few periods of no output) and added a loop (to emulate the stop/start behaviour):

        #!/bin/sh for i in {1..3}; do echo With newline echo With newline echo "NO newline\c" sleep 10 echo With newline done

        In the Perl script, I just changed

        local $SIG{ALRM} = sub { kill HUP => $pid; die "No data\n" };

        to

        local $SIG{ALRM} = sub { alarm 0; print "No data\n"; alarm $timeout };

        Here's the new output:

        With newline With newline No data No data No data NO newlineWith newline With newline With newline No data No data No data NO newlineWith newline With newline With newline No data No data No data NO newlineWith newline

        Perhaps take a look at perlipc: Signals. This has more information on Signals in general; it also has another $SIG{ALRM} example.

        -- Ken

Re: Check for a new line
by jellisii2 (Hermit) on Jan 27, 2014 at 12:25 UTC
    Instead of printing STATISTICIS directly, collect it in an array. This allows you to react to it.
      Hi, At the moment I'm just printing to the screen to test but in my loop I have done something like this:
      $STAT_LINE = "$_"; push (@TOTAL_STATS, $STAT_LINE); if (@TOTAL_STATS >= $SPF) { foreach(@TOTAL_STATS) {print "$_"}; splice @TOTAL_STATS; }

      But when there is no new line to print, the program just keeps waiting for a new line. What I want is a way to say that if there is no new line after 10 seconds push "Error" to the array

        I don't see where this changes, assuming you have the time thing sorted out. If you don't, you could try something like
        my $start_time = time(); my @data; while (time() < $start_time + 10) { # try to populate @data here } if (!@data) { print "error!\n"; } else { print @data; }

        Unless I'm misunderstanding the problem, which is entirely possible.