简体   繁体   中英

Reading data from file into an array to manipulate within Perl script

New to Perl. I need to figure out how to read from a file, separated by (:), into an array. Then I can manipulate the data.

Here is a sample of the file 'serverFile.txt' (Just threw in random #'s) The fields are Name : CPU Utilization: avgMemory Usage : disk free

 Server1:8:6:2225410
 Server2:75:68:64392
 Server3:95:90:12806
 Server4:14:7:1548700

I would like to figure out how to get each field into its appropriate array to then perform functions on. For instance, find the server with the least amount of free disk space.

The way I have it set up now, I do not think will work. So how do I put each element in each line into an array?

#!usr/bin/perl

use warnings;
use diagnostics;
use v5.26.1;

#Opens serverFile.txt or reports and error
open (my $fh, "<", "/root//Perl/serverFile.txt") 
    or die "System cannot find the file specified. $!";

#Prints out the details of the file format
sub header(){
    print "Server ** CPU Util% ** Avg Mem Usage ** Free Disk\n";
    print "-------------------------------------------------\n";
}

# Creates our variables
my ($name, $cpuUtil, $avgMemUsage, $diskFree);
my $count = 0;
my $totalMem = 0;
header();

# Loops through the program looking to see if CPU Utilization is greater than 90%
# If it is, it will print out the Server details
while(<$fh>) {
    # Puts the file contents into the variables
    ($name, $cpuUtil, $avgMemUsage, $diskFree) = split(":", $_);
    print "$name **  $cpuUtil% ** $avgMemUsage% ** $diskFree% ", "\n\n", if $cpuUtil > 90;
    $totalMem = $avgMemUsage + $totalMem;
    $count++;
}
print "The average memory usage for all servers is: ", $totalMem / $count. "%\n";

# Closes the file
close $fh;

For this use case, a hash is much better than an array.

#!/usr/bin/perl
use strict;
use feature qw{ say };
use warnings;

use List::Util qw{ min };

my %server;
while (<>) {
    chomp;
    my ($name, $cpu_utilization, $avg_memory, $disk_free)
        = split /:/;
    @{ $server{$name} }{qw{ cpu_utilization avg_memory disk_free }}
        = ($cpu_utilization, $avg_memory, $disk_free);
}

my $least_disk = min(map $server{$_}{disk_free}, keys %server);
say for grep $server{$_}{disk_free} == $least_disk, keys %server;

choroba's answer is ideal, but I think your own code could be improved

  • Don't use v5.26.1 unless you need a specific feature that is available only in the given version of Perl. Note that it also enables use strict , which should be at the top of every Perl program you write

  • die "System cannot find the file specified. $!" is wrong: there are multiple reasons why an open may fail, beyond that it "cannot be found". Your die string should include the path to the file you're trying to open; the reason for the failure is in $!

  • Don't use subroutine prototypes: they don't do what you think they do. sub header() { ... } should be just sub header { ... }

  • There's no point in declaring a subroutine only to call it a few lines later. Put your code for header in line

  • You have clearly come from another language. Declare your variables with my as late as possible. In this case only $count and $totalMem must be declared outside the while loop

  • perl will close all open file handles when the program exits. There is rarely a need for an explicit close call, which just makes your code more noisy

  • $totalMem = $avgMemUsage + $totalMem is commonly written $totalMem += $avgMemUsage

I hope that helps

To your original question about how to store the data in an array...

First, initialize an empty array outside the file read loop:

my @servers = ();

Then, within the loop, after you have your data pieces parsed out, you can store them in your array as sub-arrays (the resulting data structure is a two dimensional array):

$servers[$count] = [ $name, $cpuUtil, $avgMemUsage, $diskFree ];

Note, the square brackets on the right create the sub-array for the server's data pieces and return a reference to this new array. Also, on the left side we just use the current value of $count as an index within the @servers array and as the value increases, the size of the @servers array will grow automatically (this is called autovivification of new elements). Alternatively, you can push new elements onto the @servers array inside the loop, like this:

push @servers, [ $name, $cpuUtil, $avgMemUsage, $diskFree ];

This way, you explicitly ask for a new element to be added to the array and the square brackets still do the same creation of the sub-array.

In any case, the end result is that after you are finished with the file read loop, you now have a 2D array where you can access the first server and its disk free field (the 4-th field at index 3) like this:

my $df = $servers[0][3];

Or inspect all the servers in a loop to find the minimum disk free:

my $min_s = 0;
for ( my $s = 0; $s < @servers; $s++ ) {
    $min_s = $s  if ( $servers[$s][3] < $servers[$min_s][3] );
}
print "Server $min_s has least disk free: $servers[$min_s][3]\n";

Like @choroba suggested, you can store the server data pieces/fields in hashes, so that your code will be more readable. You can still store your list of servers in an array but the second dimension can be hash:

$servers[$count] = {
    name          => $name,
    cpu_util      => $cpuUtil,
    avg_mem_usage => $avgMemUsage,
    disk_free     => $diskFree
};

So, your resulting structure will be an array of hashes. Here, the curly braces on the right create a new hash and return the reference to it. So, you can later refer to:

my $df = $servers[0]{disk_free};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM