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

Hi Guys,

Have just started learning perl this week and it seems to be going well but I have hit a brick wall. I'm trying to do something I think should be relatively simple, I want to be able to manipulate the output of the free command on linux. All I want to do is read each line into an array so I can play with the values a little.

Is an array the most sensible approach for this? Or should I use a hash? It seems that when I do this the whole lines get put into the first part of the array, lets say $array[0] whereas I want each word to go into each part of the array. I tried using split but can't seem to find the appropriate regex to split on the values.

Can anyone help?

Cheers!

Replies are listed 'Best First'.
Re: Splitting output of a system command into an array
by FunkyMonk (Bishop) on Sep 29, 2007 at 17:42 UTC
    You seem to have the method correct: use backticks (see perlop) to capture the output of free into a scalar. Then split on whitespace to get the "words":
    my $free = `free`; my @words = split /\s+/, $free; shift @words if $words[0] eq '';

    shift removes an empty string from @words caused by $free starting with spaces.

      my @words = split /\s+/, $free;
      shift removes an empty string from @words caused by $free starting with spaces.

      Or you could just use split correctly and you wouldn't have an empty string.

      my @words = split ' ', $free;
      That's exactly what I was after, thank you. I will have to read through all my various test scripts as i'm sure I had tried that, definitely hadn't done the shift though. Also, is it possible to read each line into it's own array rather than all into one?
        Also, is it possible to read each line into it's own array rather than all into one?
        Yes, just use my @lines = `free`;

Re: Splitting output of a system command into an array
by shmem (Chancellor) on Sep 30, 2007 at 09:39 UTC
    A two-dimensional array from free(1) output:
    my @lines = `free`; for (@lines) { push @words, [ split /\s+/, $_ ]; } print $words[0]->[3],"\n";
    That prints 'free'. See perlref and perlreftut for the [ ] array reference constructor.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Splitting output of a system command into an array
by TGI (Parson) on Sep 30, 2007 at 19:00 UTC

    Generally speaking,

    • Arrays are good when you want to maintain ordering.
    • Hashes are good when you need to handle uniqueness constraints, or when you want to make multiple lookups of data based on some value.

    # Remove duplicates from an array using a hash. # create starting array. @dupes = qw( a a b b b b a c b a c c b ); # use hash slice to create hash entries. @unique{@dupes} = (); # pretty much like: # foreach( @dupes ) { # $unique{$_} = undef; # } @unique = keys %unique; print "@unique\n"; #prints # c a b # if you want keys in order you can use # @unique = sort keys %unique;


    TGI says moo

Re: Splitting output of a system command into an array
by lyklev (Pilgrim) on Sep 30, 2007 at 21:51 UTC
    Let's use them all.

    My free command shows the following lines:

    total used free shared buffers cached Mem: 189656 180144 9512 0 6976 81012
    (I show only the first two lines and output has been formatted for your screen)

    First, use backticks to execute a command; it stores the output in an array, every line is an element. The output contains newlines, which are annoying. They can be stripped using chomp:

    my @lines = `free`; # run command, store output chomp (@lines); # remove all newlines.

    Next, we want to store the names of all fields of the second line by using split. The order is used later, so we use an array.

    my @field_names = split (' ', $lines[0]); # split 1st line

    The second line contains the memory numbers. We could use split to get the different elements, but to make it more interesting, we use regular expressions, marked by the =~:

    my @mem_sizes = ( $lines[1] =~ m/(\d+)/g );

    This line says: scan line 2 for matches of one or more digits (\d+). The parentheses around the digit remembers the found number. The 'g' repeats the process, and an array of found numbers is returned.

    So now we have one array with numbers, and one array with the corresponding field names. Whenever you see 'corresponding' like this, you should think of a hash. Let's create one:

    my %free; # a hash, where the fields will get stored

    Now comes the magic:

    @free { @field_names } = @mem_sizes;

    Now, we can use the data by the field names instead of by their index, which will be easier to understand:

    print("Cached: ", $free{'cached'}, "\n");

    As an excercise left to the reader: in the line

    @free { @field_names } = @mem_sizes;

    why does it say @free, and not $free?