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

Hi Monks,

This is proboably easy for most, but as i have very little knowledge i was hoping one of you might be able to help. I want to split up the values returned by the command 'uptime' and put them into seperate values. Say '10:03pm up 3 days, 9:27, 1 user, load average: 0.00, 0.00, 0.00' is the returned value by running the command. i would like it split it up into Time, Uptime (up 3 days, 9:27), Current Users and CPU Load.
Thanks

Edit by tye

Replies are listed 'Best First'.
Re: Uptime
by Zaxo (Archbishop) on Aug 18, 2002 at 21:37 UTC

    Here's one way:

    my @uptime = split " ", qx(/usr/bin/uptime); my %uptime; @uptime{'Time', 'Uptime', 'Current Users', 'CPU Load'} = ( $uptime[0], join ' ', @uptime[1..4], join ' ', @uptime[5..6], join ' ', @uptime[7..$#uptime] );

    Update: Repaired typo, ++sauoq for the eyes. There seems to be a problem with the parsing...fixed, not pretty, but neither is uptime :-\

    After Compline,
    Zaxo

      That won't pull out the time or uptime correctly either. The time is first, separated from the uptime by space. Then the number of days and hours:minutes of uptime is separated by a comma. Zaxo's should work now that he fixed it.

      Just matching might be better than trying to use split.

      @uptime{'Time', 'Uptime', 'Users', 'Load'} = qx(/usr/bin/uptime) =~ /^\s*(\S*)\s*([^,]*,[^,]*),([^,]*),(.*)/;
      Update: Tested and fixed now.
      Update 2: sauoq sighs. Thanks guha. Now it's really fixed.
      -sauoq
      "My two cents aren't worth a dime.";
      
Re: Parsing 'uptime' output
by rob_au (Abbot) on Aug 19, 2002 at 00:58 UTC
    I generally recommend people to try to avoid parsing the output of another program when the information they require can be obtained more directly from other sources. For example, another way which may prove to be a better alternative for obtaining the uptime would be to source if 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; }

    A similar approach could be taken to obtain load average information from /proc/loadavg.

     

      For example, another way which may prove to be a better alternative for obtaining the uptime would be to source if directly from /proc/uptime (assuming that this interface is available to you)

      I understand the sentiment here and often this approach makes sense but I don't think it does in this case.

      The output from Solaris's stock uptime is close enough to GNU's that the regex would work without modification. So would IRIX's. And OpenBSD's. None of those systems has a /proc/uptime though. Using the tool in this case is far more portable.

      As the uptime program is a stable enough tool that its interface is unlikely to change, using it probably should not be regarded as a maintenance risk either.

      Getting your data from another program isn't inherently a bad thing. Nor should it to be shunned in lieu of any alternative. The key is to retrieve your data from an interface that is as stable and portable as possible. In this case, the data source he wanted to use is more standard than the one you suggested.

      -sauoq
      "My two cents aren't worth a dime.";
      
Re: Parsing 'uptime' output
by BrowserUk (Patriarch) on Aug 19, 2002 at 02:03 UTC

    If you find Zaxo's solution imaginative but complex and are put off by the sight of sauoq's daunting collection of brackets, comma's and star's that are so difficult to understand as well as get right, you might like to try:

    Start with your example string

    10:03pm up 3 days, 9:27, 1 user, load average: 0.00, 0.00, 0.00

    Put brackets around the bits you want to capture:

    (10:03pm) (up 3 days, 9:27), (1 user), (load average: 0.00, 0.00, +0.00)

    Anchor the ends: (not always strictly necessary, but it usually doesn't hurt).

    ^(10:03pm) (up 3 days, 9:27), (1 user), (load average: 0.00, 0.00, + 0.00)$

    Substitute the minimum necessary for the bits that change:

    ^(.+?m) (up .+? days, .+?:.+?), (.+? user), (load average: .*?)$

    Deal with possible plurals: (Note: the s on users)

    ^(.+?m) (up .+? days?, .+?:.+?), (.+? users?), (load average: .*?) +$

    Wrap in delimiters, bind and assign to the variables of your choice:

    ($Time, $Uptime $Current_Users,$CPU_Load) = `/usr/bin/uptime` =~ /^(.+?m) (up .+? days?, .+?:.+?), (.+? users? +), (load average: .*?)$/;

    Used on your sample input it renders:

    ($Time, $Uptime, $Current_Users,$CPU_Load) = '10:03pm up 3 days, 9:27, 1 user, load average: 0.00, 0.00, 0. +00' =~ /^(.+?m) (up .+? days?, .+?:.+?), (.+? users?), (load aver +age: .*?)$/; print '[', $Time, '] [', $Uptime, '] [', $Current_Users, '] [', $C +PU_Load, ']'; # Ouput [10:03pm] [up 3 days, 9:27] [1 user] [load average: 0.00, 0.00, 0. +00]

    Possibly not as good as the other solutions, but arguably easier to modify if you wanted to adjust it yourself.


    What's this about a "crooked mitre"? I'm good at woodwork!
      Careful. uptime's output is dependent on how much time has passed. The .+? days bit may not always be there for example. That's why I, in general, would agree with rob_au on getting the information from a more friendly interface - of course, the caveats sauoq mentioned apply here. Choose your poison.

      Makeshifts last the longest.

        Valid point, but as someone's sig. reads: "Teach a man to fish...".

        I did mention that my testing was based upon his sample uptime output. Maybe I should have also said that if the format of the output varies from that (more than the pluralisation of day(s) and user(s)), he will have to read perlre and/or perlretut to work out how to adjust it further.


        What's this about a "crooked mitre"? I'm good at woodwork!
Re: Uptime
by Anonymous Monk on Aug 18, 2002 at 21:11 UTC
    sorry, i meant to say that i would like them split up into seperate VARIABLES not values. Thanx