Perl Use of uninitialized value in string - perl

"Use of uninitialized value $str1 in string eq at ./script.pl line 20"
not sure why I get this error on line 20, if I comment 20 out, it goes away:
15 ######## HELP section ################
16 #
17 our #ARGV;
18 our ( $str1, $str2 ) = #ARGV;
19
20 if ( $str1 eq "help" ) { &help(); }
21
22 if ( !$str1 ) { &help(); }
23 #
24 sub help {
25 print <<EOF;
26 +++++++++++++++++++++++++++++++++++++++++++++++++++
27 + usage: $0 "cmd1,cmd2,cmd3" "host1,host2,host3"
28 +++++++++++++++++++++++++++++++++++++++++++++++++++
29 EOF
30 exit 0;
31 }

It's not an error, it's a warning. It means "$str1" is uninitialized, which in this case means there were no command line arguments specified to the script.
Merge lines 20 and 22 into
help() if ! $str1 || $str1 eq 'help';

Related

How to resolve this warning in Perl

I asked this type of ques previously but didn't provide the full code.
I am reading below file and checking the max word width present in each column and then write it to another file with proper alignment.
id0 id1 id2 batch
0 34 56 70
2 3647 58 72 566
4 39 616 75 98 78 78987 9876 7899 776
89 40 62 76
8 42 64 78
34 455 544 565
My code:
unlink "temp1.log";
use warnings;
use strict;
use feature 'say';
my $log1_file = "log1.log";
my $temp1 = "temp1.log";
open(IN1, "<$log1_file" ) or die "Could not open file $log1_file: $!";
my #col_lens;
while (my $line = <IN1>) {
my #fs = split " ", $line;
my #rows = #fs ;
#col_lens = map (length, #rows) if $.==1;
for my $col_idx (0..$#rows) {
my $col_len = length $rows[$col_idx];
if ($col_lens[$col_idx] < $col_len) {
$col_lens[$col_idx] = $col_len;
}
};
};
close IN1;
open(IN1, "<$log1_file" ) or die "Could not open file $log1_file: $!";
open(tempp1,"+>>$temp1") or die "Could not open file $temp1: $!";
while (my $line = <IN1>) {
my #fs = split " ", $line;
my #az;
for my $h (0..$#fs) {
my $len = length $fs[$h];
my $blk_len = $col_lens[$h]+1;
my $right = $blk_len - $len;
$az[$h] = (" ") . $fs[$h] . ( " " x $right );
}
say tempp1 (join "|",#az);
};
My warning
Use of uninitialized value in numeric lt (<) at new.pl line 25, <IN1> line 3.
Use of uninitialized value in numeric lt (<) at new.pl line 25, <IN1> line 4.
Use of uninitialized value in numeric lt (<) at new.pl line 25, <IN1> line 4.
Use of uninitialized value in numeric lt (<) at new.pl line 25, <IN1> line 4.
Use of uninitialized value in numeric lt (<) at new.pl line 25, <IN1> line 4.
Use of uninitialized value in numeric lt (<) at new.pl line 25, <IN1> line 4.
I am getting the output correctly but don't know how to remove this warnings.
$col_idx can be up to the number of fields on a line, minus one. For the third line, this is more than the highest index of #col_lens, which contains at most 3 elements. So doing the following makes no sense:
if ($col_lens[$col_idx] < $col_len) {
$col_lens[$col_idx] = $col_len;
}
Replace it with
if (!defined($col_lens[$col_idx]) || $col_lens[$col_idx] < $col_len) {
$col_lens[$col_idx] = $col_len;
}
With this, there's really no point checking for $. == 1 anymore.
You're getting uninitialized warning because, while checking the $col_lens[$col_idx] < $col_len condition, one or both of them are undef.
Solution 1:
You can skip checking this condition by the use of next statement.
for my $col_idx (0..$#rows) {
my $col_len = length $rows[$col_idx];
next unless $col_lens[$col_idx];
if ($col_lens[$col_idx] < $col_len) {
$col_lens[$col_idx] = $col_len;
}
}
Solution 2: (Not advised):
You can simply ignore Use of uninitialized value.. warnings by putting this line at top of your script. This will disable uninitialized warnings in a block.
no warnings 'uninitialized';
For more info, please refer this link
Following code demonstrates one of many possible ways for solution to this task
read line by line
get length of each field
compare with stored earlier
adjust to max length
form $format string for print
print formatted data
use strict;
use warnings;
use feature 'say';
my(#data,#length,$format);
while ( <DATA> ) {
my #e = split ' ';
my #l = map{ length } #e;
$length[$_] = ($length[$_] // 0) < $l[$_] ? $l[$_] : $length[$_] for 0..$#e;
push #data,\#e;
}
$format = join ' ', map{ '%'.$_.'s' } #length;
$format .= "\n";
for my $row ( #data ) {
printf $format, map { $row->[$_] // '' } 0..$#length;;
}
__DATA__
id0 id1 id2 batch
0 34 56 70
2 3647 58 72 566
4 39 616 75 98 78 78987 9876 7899 776
89 40 62 76
8 42 64 78
34 455 544 565
Output
id0 id1 id2 batch
0 34 56 70
2 3647 58 72 566
4 39 616 75 98 78 78987 9876 7899 776
89 40 62 76
8 42 64 78
34 455 544 565

Perl: read an array and calculate corresponding percentile

I am trying to code for a perl code that reads a text file with a series of number, calculates, and prints out the numbers that corresponds to the percentiles. I do not have access to the other statistical modules, so I'd like to stick with just pure perl coding. Thanks in advance!
The input text file looks like:
197
98
251
82
51
272
154
167
38
280
157
212
188
88
40
229
228
125
292
235
67
70
127
26
279
.... (and so on)
The code I have is:
#!/usr/bin/perl
use strict;
use warnings;
my #data;
open (my $fh, "<", "testing2.txt")
or die "Cannot open: $!\n";
while (<$fh>){
push #data, $_;
}
close $fh;
my %count;
foreach my $datum (#data) {
++$count{$datum};
}
my %percentile;
my $total = 0;
foreach my $datum (sort { $a <=> $b } keys %count) {
$total += $count{$datum};
$percentile{$datum} = $total / #data;
# percentile subject to change
if ($percentile{$datum} <= 0.10) {
print "$datum : $percentile{$datum}\n\n";
}
}
My desired output:
2 : 0.01
3 : 0.01333
4 : 0.01666
6 : 0.02
8 : 0.03
10 : 0.037
12 : 0.04
14 : 0.05
15 : 0.05333
16 : 0.06
18 : 0.06333
21 : 0.07333
22 : 0.08
25 : 0.09
26 : 0.09666
Where the format is #number from the list : #corresponding percentile
To store the numer wihtout a newline in #data, just add chomp; before pushing it, or chomp #data; after you've read them all.
If your input file has MSWin style newlines, convert it to *nix style using dos2unix or fromdos.
Also, try to learn how to indent your code, it boosts readability. And consider renaming $total to $running_total, as you use the value as it changes.

How to print common values from two different overlapping ranges without repetition

I have the following code. I am trying to print all common values from #arr2 and #arr4 without repetition. The expected output should be 5,6,7,8,9,13,14,15,16,17,18. I am not getting how to put a condition in a loop to avoid repetition and why $i is not printing in this code.
#!/usr/bin/perl
my #arr2 = ( 1 .. 10, 5 .. 15, 10 .. 20 );
my #arr4 = ( 5 .. 9, 13 .. 18 );
foreach my $line1 (#arr2) {
my ( $from1, $to1 ) = split( /\.\./, $line1 );
#print "$to1\n";
foreach my $line2 (#arr4) {
my ( $from2, $to2 ) = split( /\.\./, $line2 );
for ( my $i = $from1; $i <= $to1; $i++ ) {
for ( my $j = $from2; $j <= $to2; $j++ ) {
if ( $i == $j ) {
print "$i \n";
}
}
}
}
}
As Jonathan has pointed out, you appear to misunderstanding the nature of your data because you don't recognize the Range Operator .. used to construct lists.
my #array = (1 .. 10);
print "#array\n";
Outputs
1 2 3 4 5 6 7 8 9 10
Once you recognize that, then you just need to be pointed to the following:
perlfaq4 - How can I remove duplicate elements from a list or array?
perlfaq4 - How do I compute the difference of two arrays? How do I compute the intersection of two arrays?
Combined to form:
#!/usr/bin/perl
use strict;
use warnings;
my #arr2 = ( 1 .. 10, 5 .. 15, 10 .. 20 );
my #arr4 = ( 5 .. 9, 13 .. 18 );
my %seen;
$seen{$_}++ for uniq(#arr2), uniq(#arr4);
my #intersection = sort { $a <=> $b } grep { $seen{$_} == 2 } keys %seen;
print "#intersection\n";
sub uniq {
my %seen;
$seen{$_}++ for #_;
return keys %seen;
}
Outputs:
5 6 7 8 9 13 14 15 16 17 18
The first step to understanding your problem is to understand your data — the arrays do not hold what you think they hold.
#!/usr/bin/perl
my #arr2=(1..10,5..15,10..20);
my #arr4=(5..9,13..18);
print "arr2: #arr2\n";
print "arr4: #arr4\n";
The output from this is:
arr2: 1 2 3 4 5 6 7 8 9 10 5 6 7 8 9 10 11 12 13 14 15 10 11 12 13 14 15 16 17 18 19 20
arr4: 5 6 7 8 9 13 14 15 16 17 18
This shows that your code trying to split a string on .. is going to fail horribly.
One of the most basic debugging techniques is printing out the data you've actually got to ensure it matches what you think you should have. Here, that basic printing would have shown that the input data is not in the format you expected.

Represent first 8 characters of the string as hex numbers separated by spaces

I need to represent first 8 characters of the string as hex numbers separated by spaces.
For example:
"This is the test!" converts to "54 68 69 73 20 69 73 20"
I use the following code to do it. Is there better(simpler) way to do it in Perl?
my $hex = unpack( "H16", $string );
my $hexOut = "";
for ( my $i = 0 ; $i < length($hex) ; $i += 2 )
{
$hexOut .= substr( $hex, $i, 2 ) . " ";
}
$hexOut = substr( $hexOut, 0, -1 );
I can't resist submitting a Perl one-liner!
my $string = "This is a test";
print(join(' ', unpack("(A2)*", unpack( "H16", $string ))) . "\n");
If you split on null, you get a list of bytes. Then just print them in hexadecimal.
use strict;
use warnings;
my $string = shift // 'This is the test!';
my #bytes = split //, $string;
for my $i (0..7) {
printf "%02X ", ord $bytes[$i];
}
print "\n";
But if you really want characters rather than bytes, then unpack.
my #chars = unpack "C0U*", $string;
for my $i (0..7) {
printf "%02X ", $chars[$i];
}
print "\n";
For the test string, it's the same
$ ./leon01.pl
54 68 69 73 20 69 73 20
54 68 69 73 20 69 73 20
but in general, it's not
$ ./leon01.pl 'A Møøse once bit my sister.'
41 20 4D C3 B8 C3 B8 73
41 20 4D F8 F8 73 65 20
$ ./leon01.pl '① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩'
E2 91 A0 20 E2 91 A1 20
2460 20 2461 20 2462 20 2463 20
my $string = "This is the test!";
my $hex_string = sprintf("%vx", substr($string, 0, 8));
$hex_string =~ y/./ /;
print $hex_string, "\n";
(The v modifier is a perl-specific extension to printf formats, introduced sometime in 5.8.x IIRC.)
I'll let you decide if this is better or not. Just another way to do it. ;-)
#! /usr/bin/perl -w
$string = "This is the test!";
$strLength = length($string);
#bytes = unpack(A2 x $strLength,unpack("H16",$string));
print "#bytes\n";
# Also could change it back to a string w/spaces:
$pretty = join(" ",#bytes);
print $pretty;

perl print formatting question

I want to display a table in perl, the rows and column names for which will be of variable length. I want the columns to be neatly aligned. The problem is the row and column heading are of variable length, so the alignment shifts off for different files.
Here is the code I am using to format :
print "\n ";
foreach (keys(%senseToSenseCountHash))
{
printf "%15s",$_;
}
print "\n";
print "------------------------------------------------------------\n";
my $space = "---";
foreach my $realSense (keys(%actualSenseToWronglyDisambiguatedSense))
{
printf "%s",$realSense;
foreach (keys(%senseToSenseCountHash))
{
if(exists($actualSenseToWronglyDisambiguatedSense{$realSense}[0]{$_}))
{
printf "%15s",$actualSenseToWronglyDisambiguatedSense{$realSense}[0]{$_};
}
else
{
printf "%15s",$space;
}
}
print "\n";
}
The outputs I get are as follows (for different files that I have to test on) :
Microsoft IBM
------------------------------------------------------------
Microsoft 896 120
IBM 66 661
SERVE12 SERVE2 SERVE6 SERVE10
------------------------------------------------------------
SERVE12 319 32 19 8
SERVE2 44 159 39 25
SERVE6 22 9 102 1
SERVE10 14 16 12 494
HARD3 HARD2 HARD1
------------------------------------------------------------
HARD3 68 7 27
HARD2 6 60 90
HARD1 37 69 937
I want to make this output aligned regardless of the row and column name. Can anyone please help?
Thanks so much!
This line:
printf "%s",$realSense;
has no specific width, and is throwing off the alignment.
Found the answer, pasting it here in case any one wants to use it.
printf "%10s %-2s",'----------','|';
foreach(keys(%senseToSenseCountHash))
{
printf "%s",'----------------';
}
print "\n";
printf "%10s %-2s",' ','|';
foreach(keys(%senseToSenseCountHash))
{
printf "%-14s",$_;
}
print "\n";
printf "%10s %-2s",'----------','|';
foreach(keys(%senseToSenseCountHash))
{
printf "%s",'----------------';
}
print "\n";
foreach my $key (sort { $senseToSenseCountHash{$b} <=>
$senseToSenseCountHash{$a} } keys %senseToSenseCountHash )
{
$maxSense = $senseToSenseCountHash{$key};
last;
}
my $space = "---";
foreach my $realSense (keys(%actualSenseToWronglyDisambiguatedSense))
{
printf "%-10s %-2s",$realSense,'|';
foreach (keys(%senseToSenseCountHash))
{
if(exists($actualSenseToWronglyDisambiguatedSense{$realSense}[0]{$_}))
{
printf "%-15s",$actualSenseToWronglyDisambiguatedSense{$realSense}[0]{$_};
}
else
{
printf "%-15s",$space;
}
}
print "\n";
}
printf "%10s %-2s",'----------','|';
foreach(keys(%senseToSenseCountHash))
{
printf "%s",'----------------';
}
print "\n";
Output :
---------- | ------------------------------------------------
| HARD3 HARD2 HARD1
---------- | ------------------------------------------------
HARD3 | 68 7 27
HARD2 | 6 60 90
HARD1 | 37 69 937
---------- | ------------------------------------------------
---------- | ----------------------------------------------------------------
| SERVE12 SERVE2 SERVE6 SERVE10
---------- | ----------------------------------------------------------------
SERVE12 | 319 32 19 8
SERVE2 | 44 159 39 25
SERVE6 | 22 9 102 1
SERVE10 | 14 16 12 494
---------- | ----------------------------------------------------------------