get param function array in perl - arrays

I want to get the array that I send in my function but it seem's to be empty.
I call send_file(); with an array in the param
send_file($addr, #curfile);
And this is the way I get back the param
sub send_file($$)
{
my $addr = $_[0];
my #elem = #_;
...
}
Why my #elem is empty ? How could I get back the array without losing everything ?

Don't use prototypes. Their purpose is to change parsing of the source which you don't need.
sub send_file
{
my $addr = shift;
my #elem = #_;
...
}
send_file($addr, #curfile);

You should pass your array by reference instead:
#!/usr/bin/perl
use strict;
use warnings;
my $test_scalar = 10;
my #test_array = qw(this is a test);
sub test($\#)
{
my ($scalar, $array) = #_;
print "SCALAR = $scalar\n";
print "ARRAY = #$array\n";
}
test($test_scalar, #test_array);
system 'pause';
Output:
SCALAR = 10
ARRAY = this is a test
Press any key to continue . . .
EDIT:
If you would like to do the same thing without passing by reference change your $$ to $# and use shift so the first argument doesn't end up included in your array. Passing arrays by reference is better coding practice though . . . This is just to show you how it can be done without passing by reference:
#!/usr/bin/perl
use strict;
use warnings;
my $test_scalar = 10;
my #test_array = qw(this is a test);
sub test($#)
{
my ($scalar, #array) = #_;
print "SCALAR = $scalar\n";
print "ARRAY = #array\n";
}
test($test_scalar, #test_array);
system 'pause';
This will get you the same output.
You can also get rid of the $# altogether if you would like it really isn't necessary.

Why my #elem is empty ?
Your #elem is not empty, it has exactly two elements. First is value of $addr and second is size/number of elements in #curfile array. This is due $$ prototype definition which requires two scalars, so scalar #curfile is passed as second parameter.
How could I get back the array without loosing everything ?
Since you're not using prototype advantages, just omit prototype part,
sub send_file {
my ($addr, #elem) = #_;
...
}

Related

Passing array arguments to Perl subroutine

I am new to perl programming and I am trying to build a script using several subroutines on it. For a start I am trying to run a short mocke script to work out subroutines behaviour, but I don't get to understand the input.
Here is my code:
sub prueba{
my (#array1, #array2)=#_;
if (scalar(#array1)<scalar(#array2)) {
print #array1,"\n";
} elsif (scalar(#array1)>scalar(#array2)){
print #array2,"\n";
}
};
my #primero=(1,5,9);
my #segundo=(1,7,8,9,6,5,6,9);
prueba(#primero,#segundo);
I am passing two arrays and I want the subroutine to retrieve the answer according to those arrays, but when I run it I get no output, not even warning errors messages... I already tried using the refference to the array, but still not working:
sub prueba{
my (#array1, #array2)=#_;
if (scalar(#array1)<scalar(#array2)) {
print #array1,"\n";
} elsif (scalar(#array1)>scalar(#array2)){
print #array2,"\n";
}
};
my #primero=(1,5,9);
my #segundo=(1,7,8,9,6,5,6,9);
prueba(\#primero,\#segundo);
You can't pass arrays to subs (and they can't return them). You can only pass a number of scalars. What you are doing is equivalent to the following:
prueba(1,5,9,1,7,8,9,6,5,6,9);
All of the arguments end up in #array1. What we do is pass references to arrays.
prueba(\#primero,\#segundo);
But that also requires changing the sub. Without change, all of the arguments still end up in #array1. See perlreftut for a start on working with references. You can use
sub prueba{
my ($array1, $array2)=#_;
if (scalar(#$array1)<scalar(#$array2)) {
print "#$array1\n";
} elsif (scalar(#$array1)>scalar(#$array2)){
print "#$array2\n";
}
}
or just
sub prueba {
my ($array1, $array2) = #_;
if (#$array1 < #$array2) { say "#$array1"; }
elsif (#$array1 > #$array2) { say "#$array2"; }
}
< and > expect a number, so they already impose scalar context. And might as well use say, though that requires use feature qw( say ); (or something like use 5.014; which does the trick as well).
You can use prototypes to make it look like you're passing multiple arrays, and have perl turn them automatically into references:
sub prueba :prototype(\#\#) {
my ($array1, $array2) = #_;
if (#$array1 < #$array2) {
print #$array1,"\n";
} elsif (#$array1 > #$array2){
print #$array2,"\n";
}
}
my #primero=(1,5,9);
my #segundo=(1,7,8,9,6,5,6,9);
prueba(#primero, #segundo);
But read the documentation carefully to understand the cases where the subroutine can be called without enforcing the prototype.
Thanks all I just figured out what I wanted. I found that I can actually pass an array to perl, however maybe I am not explaning mysfel properly.The thing is to load the arrays as follow, inside the subroutine.
my #primero=#{$_[0]};
my #segundo=#{$_[1]};
This means we are using the reference. Ehen running the function, we must write the \ before each input:
prueba(\#primero,\#segundo);

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};

Perl array of hashrefs problems

I have this in my project:
sub get_src_info($) {
my $package = shift;
my ($key,$value,$tail) =("","","");
my (#APT_INFO,$r);
open APT, "apt-cache showsrc $package|";
while (<APT>){
chomp;
($key,$value,$tail) = split /:/;
if (defined ($key) && defined ($value)) {
$value =~ s/^\s+|\s+$//;
if (defined($tail)) {
$value = "$value:$tail";
}
if ( $key eq "Package" or $key eq "Version" ) {
$r->{$key} = $value;
}
}
if(/^$/) { push #APT_INFO,$r++; }
}
close APT;
return #APT_INFO;
}
I generally use use strict to check for errors.
The code works with no strict "refs"; instruction, but fails to run without it, yielding error:
Can't use string ("163277181") as a HASH ref while "strict refs" in use at a.pl line 61, <APT> line 45.
line 61 is: $r->{$key} = $value;
I prefer to fix my code, rather than silence it, but can't get what's wrong/how to fix this.
Also, what's correct way to advance a reference to point to the next object? Although it works I do not feel like $r++ is correct construction in here.
thanks a lot in advance.
You use the $r variable both as a hash reference $r->{$key}, and a number $r++. References numify to an ID, so you can use them as numbers. However, you cannot use a non-reference scalar as a reference (references are not pointers). To make this clear:
my $reference = { foo => 1 };
my $numified = 0 + $reference; # is now 163277181
say $numified->{foo}; # does not work under strict "refs"
# ${"163277181"}{foo} is equivalent: This looks for global variable %163277181
You can work around the problems by simply creating a new reference in $r when a new block begins. You should also scope your other variables properly: do not use globals like APT, and declare your $key etc. variables inside the loop.
Also, $key can never be undef (split doesn't return undef values), and you should not use prototypes.
I guess the following code will do what you want:
use autodie; # automatic error handling, e.g. for `open`
sub get_src_info {
my ($package) = #_;
my $info = {};
my #apt_info;
open my $fh, "-|", "apt", "showsrc", $package; # circumvent the shell for safety
while (<$fh>) {
chomp;
unless (length) {
push #apt_info, $info;
$info = {}; # put a new reference into $info
next;
}
my ($key, $value) = split /:/, $_, 2; # limit number of fragments to 2
next unless $key eq "Package" or $key eq "Version";
s/^\s+//, s/\s+$// for $value; # this trims at *both* ends
$info->{$key} = $value;
}
return #apt_info;
}
More on References vs. Pointers:
You cannot use pointer arithmetic in memory safe languages. Perl is such a memory safe language. The references which you can use are similar to pointers in C. They are typed as well (although dynamically), but are reference counted automatically, so that the referenced data structure is freed once it isn't needed. Proponents of garbage collection and memory safety point to less errors (e.g. double free) and increased productivity, although some algorithms may not be expressed as elegantly. Most modern languages are memory safe by default.
Even if this were C, and $r++ would point to a new address, I'd have to ask you: Shouldn't you malloc new memory? Memory allocation is implicit in Perl. The $r = {} gives us a new, empty hash reference. If you use lexical variables (with my), deallocation happens automatically as well.

How to pass index of array in cmd line argument which is function pointer and call to specific function in perl?

Suppose I have 2 functions in Perl. I would create a array of references of that two functions. & in command line argument I'll pass only that index of array which call specific function and if I don't give any argument then it'll call all functions which referenced were in array(Default case).
So, can any help me to do this?
## Array content function pointers
my #list= {$Ref0,$Ref1 }
my $fun0Name = “fun0”;
my $Ref0 =&{$fun0Name}();
my $fun1Name = “fun1”;
my $Ref1 =&{$fun1Name}();
#### Two functions
sub fun0() {
print "hi \n";
}
sub fun1() {
print "hello \n";
}
##### Now in cmd argument if i passed Test.pl -t 0(index of array ,means call to 1st function)
##### if i give test.pl -t (No option ) ....then i call both function.
Creating a function pointer (called a code reference in Perl) is easy enough:
sub foo {
say "foo!";
}
sub bar {
say "bar!";
}
my $foo_ref = \&foo;
my $bar_ref = \&bar;
Putting things in an array is pretty easy:
my #array = ( $foo_ref, $bar_ref );
Reading arguments from the command line is pretty easy:
my $arg = shift #ARGV;
Looking things up in an array is also pretty easy:
my $item = $array[$arg];
Which part are you having trouble with?

What is the value if you shift beyond the last element of an array?

In this piece of code, shift is used twice, even though the method only takes one parameter:
sub regexVerify ($)
{
my $re = shift;
return sub
{
local $_ = shift;
m/$re/ ? $_ : undef;
};
}
What does this make the value of local $_, once shift is used again? I was (perhaps naively) assuming that shifting into nothingness would result in undef. But if that were true, this line has no meaning, right?:
m/$re/ ? $_ : undef;
The above sub is called like:
regexVerify (qr/^([a-z].*)?$/i);
The second shift is inside the inner sub declaration. That scope will have an entirely new #_ to work with, which won't have anything to do with the #_ passed to the outer subroutine.
regexVerify is a subroutine that returns another subroutine. Presumably you would later invoke that subroutine with an argument:
my $func = regexVerify(qr/^([a-z].*)?$/i);
# $func is now a "code reference" or "anonymous subroutine"
...
if ($func->($foo)) { # invoke the subroutine stored in $func with arg ($foo)
print "$foo is verified.\n";
} else {
print "$foo is not verified!\n";
}
local $_ = shift; doesn't get executed until you make call to anonymous function. ie
my $anon_func = regexVerify (qr/^([a-z].*)?$/i);
# NOW sending arguments in #_ for local $_ = shift;
print $anon_func->("some string");

Resources