Using map on an array with sprintf - perl

I have the following code:
my #some_range = (6..10);
my #some_range_new = map(sprintf("I%03d ", $_), \#some_range);
say join(' ', #some_range_new)
I would expect the output to be:
I006 I007 I008 I009 I010
but instead I get:
275738152
Why?
Note: I know that I can do my #some_range_new = ("I006".."I010"), but I am trying to learn to use map in combination with sprintf

You use a reference to #some_range in your map statement. I'm not sure why you thought that would work.
The correct way is
map sprintf("I%03d", $_), #some_range;
In short, just remove the backslash and your code will work.
The reason it fails is that \#some_range will be interpreted as a number by %d. When an array reference is interpreted as a number, it evaluates to the address of the referenced array. For example:
$ perl -lwe '#a=1..10; $x = \#a; print $x; printf "I%03d", $x'
ARRAY(0x468c18)
I4623384

Related

Ip to hexa formatting

i need to convert decimal ip to hexa value.
example:110.1.1.3 to 6e01:103.
But by using below code i am getting it in 6e01103. I need it either 6e01:103 or 6e:01:103 format. And then need to concatenate with hexa value 64:ff9b::, my end output needd to be 64:ff9b::6e01:103. Kindly help me in this.
sub ip_hexa($){
my $ip = shift;
my #octets = split /\./, $ip;
my $result;
foreach (#octets){
$hexa_ip = join":",printf("%02x", "$_");
}
return $hexa_ip;
}
I'm not completely certain about the output you want, but there are a few issues with the code which I'll list below:
The $ in the function declaration is not required. It sets the function's prototype which most likely does not do what you think it does. See perlsub for details.
$hexa_ip should be declared before being used as good practice to prevent hard to find errors. Perhaps you meant my $hexa_ip instead of my $result? In any case, use use strict at the start of the program to catch such errors.
printf() prints to screen and only returns a boolean. Look at sprintf for the right function to use.
join() is not being used correctly. See join.
# 6e01:103
sprintf "%x:%x",
unpack 'nn',
pack 'C4',
split /\./,
'110.1.1.3'
# 6e:01:103
sprintf "%x:%x:%x",
unpack 'CCn',
pack 'C4',
split /\./,
'110.1.1.3'
The sprintf lines can be replaced with join ':', map sprintf '%x',

multidimensional array: argument isn't numeric in array element

OS: AIX
Shell: KSH
Following the accepted answer on this question I have created an multimensional array. Only, I get an error while trying to print the content of the array.
Error:
Argument "content of $pvid" isn't numeric in array element at...
The script:
#!/usr/bin/perl
use warnings;
use strict;
use Term::ANSIColor;
my #arrpvid = ();
print colored( sprintf("%-10s %9s %8s %8s %8s", 'PVID', 'AIX', 'VIO', 'VTD', 'VHOST'), 'green' ), "\n";
foreach my $pvid (`lspv | awk '{print \$2'}`) {
foreach my $hdaix (`lspv | awk '{print \$1'}`) {
chomp $pvid;
chomp $hdaix;
push #{ $arrpvid[$pvid] }, $hdaix;
}
}
print $arrpvid[0][0];
Some explanation:
Basically I want to print 5 variables of 5 different arrays next to each other. The code is written only for 2 arrays.
The content of $pvid:
00088da343b00d9b
00088da38100f93c
The content of $hdaix:
hdisk0
hdisk1
Quick Fix
Looks like you want to use a hash rather than an array, making your inner push
push #{ $arrpvid{$pvid} }, $hdaix;
Note the change from square brackets to curly braces immediately surrounding $pvid. This tells the compiler that you want %arrpvid and not #arrpvid, so be sure to tweak your my declaration as well.
At the end to print the contents of %arrpvid, use
foreach my $pvid (sort { hex $a <=> hex $b } keys %arrpvid) {
local $" = "]["; # handy trick due to mjd
print "$pvid: [#{$arrpvid{$pvid}}]\n";
}
The Data::Dumper module is quick and easy output tool.
use Data::Dumper;
$Data::Dumper::Indent = $Data::Dumper::Terse = 1;
print Dumper \%arrpvid;
More Details
You might be tempted to obtain the numeric value corresponding to each hexadecimal string in $pvid with hex as in
push #{ $arrpvid[hex $pvid] }, ...
but given the large example values in your question, #arrpvid would become enormous. Use a hash to create a sparse array instead.
Be sure that all the values of $pvid have the same padding. Otherwise, like values may not hash together appropriately. If you need to normalize, use code along the lines of
$pvid = sprintf "%016x", hex $pvid;
The problem lies in:
push #{ $arrpvid[$pvid] }, $hdaix;
The $pvid should be a numeric value like 0 or 5 and not i.e. 00088da343b00d9b

How to get sub array?

I have the code below:
#a = ((1,2,3),("test","hello"));
print #a[1]
I was expecting it to print
testhello
But it gives me 2.
Sorry for the newbie question (Perl is a bit unnatural to me) but why does it happen and how can I get the result I want?
The way Perl constructs #a is such that it is equivalent to your writing,
#a = (1,2,3,"test","hello");
And that is why when you ask for the value at index 1 by writing #a[1] (really should be $a[1]), you get 2. To demonstrate this, if you were to do the following,
use strict;
use warnings;
my #a = ((1,2,3), ("test","hello"));
my #b = (1,2,3,"test","hello");
print "#a\n";
print "#b\n";
Both print the same line,
1 2 3 test hello
1 2 3 test hello
What you want is to create anonymous arrays within your array - something like this,
my #c = ([1,2,3], ["test","hello"]);
Then if you write the following,
use Data::Dumper;
print Dumper $c[1];
You will see this printed,
$VAR1 = [
'test',
'hello'
];
Perl lists are one-dimensional only, which means (1,2,(3,4)) is automatically flattened to (1,2,3,4). If you want a multidimensional array, you must use references for any dimension beyond the first (which are stored in scalars).
You can get any anonymous array reference with bracket notation [1,2,3,4] or reference an existing array with a backslash my $ref = \#somearray.
So a construct such as my $aref = [1,2,[3,4]] is an array reference in which the first element of the referenced array is 1, the second element is 2, and the third element is another array reference.
(I find when working with multidimensional arrays, that it's less confusing just to use references even for the first dimension, but my #array = (1,2,[3,4]) is fine too.)
By the way, when you stringify a perl reference, you get some gibberish indicating the type of reference and the memory location, like "ARRAY(0x7f977b02ac58)".
Dereference an array reference to an array with #, or get a specific element of the reference with ->.
Example:
my $ref = ['A','B',['C','D']];
print $ref; # prints ARRAY(0x001)
print join ',', #{$ref}; # prints A,B,ARRAY(0x002)
print join ',', #$ref; # prints A,B,ARRAY(0x002) (shortcut for above)
print $ref->[0]; # prints A
print $ref->[1]; # prints B
print $ref->[2]; # prints ARRAY(0x002)
print $ref->[2]->[0]; # prints C
print $ref->[2][0]; # prints C (shortcut for above)
print $ref->[2][1] # prints D
print join ',', #{$ref->[2]}; # prints C,D
I think you're after an array of arrays. So, you need to create an array of array references by using square brackets, like this:
#a = ([1,2,3],["test","hello"]);
Then you can print the second array as follows:
print #{$a[1]};
Which will give you the output you were expecting: testhello
It's just a matter of wrong syntax:
print $a[1]

perl, saving the command line option

I have this perl script and it takes in arguments using the getoption package.
Is there an easy way to document what was the exact command the user used to execute?
I would like to document into a log file.
Gordon
Use $0 and #ARGV together:
my $full_command = join(' ', $0, #ARGV);
or you can simply
my $full;
BEGIN { $full = "$0 #ARGV" }
#
print "log: $full\n";
form the perlvar
$LIST_SEPARATOR
$"
When an array or an array slice is
interpolated into a double-quoted
string or a similar context such as
/.../ , its elements are separated by
this value. Default is a space. For
example, this:
print "The array is: #array\n";
is equivalent to this:
print "The array is: " . join($", #array) . "\n";

Perl Array values show in different line without using loop

Could anyone tell me how could I print values of array in different rows without using loop?
#!/usr/bin/perl -w
my #a = ('Test1','Test2','Test3');
print "#a";# output like **Test1 Test2 Test3** but i want **Test2 in next line and Test3 next to next line**
Is it Possible?
You can just do:
print join("\n", #ar);
You can set $" variable
$" = "\n";
It's probably better to do
{
local $" = "\n";
print "#ar";
}
EDIT:
according to the camel book :
$" (or the alternative $LIST_SEPERATOR) specifies the string to put between individual elements when an array is interpolated into a double-quoted string, this for the case you want to say:
print "#ar";
$, (or the alternative $OUTPUT_FIELD_SEPERATOR) specifies the string to put between individual elements when you want to print a list. It's initially empty. You can set $, for the case you want to say:
print #ar;
You can set the $, special variable to be whatever you want to separate your list elements. This should do what you want:
$, = "\n";
my #a = ('Test1','Test2','Test3');
print #a;
use map function
print #array = map{"$_\n"} #a;