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

no matter how hard I try I just can't get my head around pattern matching!

I need to get up time out of this
6:37PM up 4 days, 2:05, 2 users, load averages: 1.99, 1.65, 1.47

so in this case I would want to match out
4 days, 2:05

can anyone do this for me?

perl is fine, very useful, but I just can't get pattern matching, anyone know any programs that will take a string and help me out to match it?

thanks so much, I know you guys can do this with your eyes closed!

Replies are listed 'Best First'.
Re: pattern matching
by stajich (Chaplain) on Jun 04, 2002 at 01:22 UTC
    This should get you going - I'm not sure what happens when uptime > 365 days if it converts to years too. I'll have to login into one of my UN*X boxes to see =)
    #!/usr/bin/perl -w use strict; # test on a string with just hours my $uptime = ' 9:12pm up 2:13, 5 users, load average: 0.84, 0.62, + 1.03'; if( $uptime =~ /up\s+((\d+) days,\s+)?(\S+),/ ) { my ($dayup,$timeup) = ( $2,$3); # $1 is the first enclosing parens + which we don't want print "timeup is ", defined $dayup ? " $dayup days and " : '', "$t +imeup hours\n"; } # test again on a string with days $uptime = "6:37PM up 4 days, 2:05, 2 users, load averages: 1.99, 1.65, + 1.47"; if( $uptime =~ /up\s+((\d+) days,\s+)?(\S+),/ ) { my ($dayup,$timeup) = ( $2,$3); # $1 is the first enclosing parens + which we don't want print "timeup is ", defined $dayup ? " $dayup days and " : '', "$t +imeup hours\n"; }
    Another way to attack this if you aren't comfortable with those regexps is to strip away the things you know you don't need and just get what you know you want ... Everything between 'up' and 'XX users'.
    my $uptime = ' 9:12pm up 2:13, 5 users, load average: 0.84, 0.62, + 1.03'; my ($keep) = ( $uptime =~ /up\s+(.+),\s+\d+\s+users/);
    HTH.
      Thanks perl monks, you guys are so helpful, thanks does anyone have any helpful hints to help me with this?
Re: pattern matching
by rob_au (Abbot) on Jun 04, 2002 at 01:17 UTC
    Another way which may prove to be other a better alternative is to read the uptime directly from /proc/uptime (assuming that this interface is available to you) - This allows for vagarities of uptime output and regular pattern matching to sidestepped altogether.

    For example:

    use Date::Calc qw/ Normalize_DHMS /; use IO::File; # get_uptime function - returns uptime from /proc/uptime # as 4-member array representing current system uptime in # days, hours, minutes and seconds sub get_uptime { my $uptime; my $fh = IO::File->new('/proc/uptime', 'r'); $uptime = (split /\s+/, <$fh>)[0] if defined $fh; return Normalize_DHMS(0, 0, 0, $uptime) if defined $uptime; }

     

Re: pattern matching
by Dog and Pony (Priest) on Jun 04, 2002 at 00:18 UTC
    my ($uptime) = /(\d+ days?, \d+:\d+)/;
    Should take care of most of your cases, including 1 day.

    (This assumes that your original string is in the special variable $_ ).


    You have moved into a dark place.
    It is pitch black. You are likely to be eaten by a grue.
      sorry I forget to mention, this could be less than a day!
Re: pattern matching
by DamnDirtyApe (Curate) on Jun 04, 2002 at 07:04 UTC

    If you need to do anything other than display the results once you've isolated the uptime, you'll probably want a more useful value, such as seconds of uptime. Here's a script that will take an uptime statement and tell you the uptime in seconds. As with Stajich's solution, this may only work on uptimes of less than one year.

    #! /usr/bin/perl use strict ; use warnings ; use Parse::RecDescent ; my $rules = q{ startrule: current_time 'up' uptime remainder eol { $return = $item[3] ; } current_time: /\d+:\d+[AP]M/ uptime: days(?) time { $return = (@{$item[1]})[0] + $item[2] ; } days: number_days 'days,' { $return = $item[1] * 86_400 ; } number_days: /\d+/ time: hours ':' minutes { $return = $item[1] + $item[3] ; } hours: /\d+/ { $return = $item[1] * 3_600 ; } minutes: /\d+/ { $return = $item[1] * 60 ; } remainder: /.+/ eol: /^\Z/ } ; my $parser = new Parse::RecDescent( $rules ) ; my @tests = ( '11:22PM up 4:30, 2 users, load averages: 0.25, 0.42, 0.40', '6:37PM up 4 days, 2:05, 2 users, load averages: 1.99, 1.65, 1.47' ) ; foreach my $test ( @tests ) { print $test . ': ' . $parser->startrule( $test ) . "\n"; }
    Have fun!
    _______________
    D a m n D i r t y A p e
    Home Node | Email