perl modifying hash in sub - perl

I'm having trouble understanding how references work with hashes in subs.
In this code, I try to change %config inside the handleOptions() subroutine :
sub handleOption;
my %config = ( gpg => "",
output => "",
pass => "",
host => "",
type => "");
handleOptions(\%config);
print "\n";
print Dumper \%config;
sub handleOptions
{
my ($gpgpath,$type,$pass,$host);
my $pConfig=#_;
GetOptions ("gpg=s" => \$gpgpath,
"type=s" => \$type,
"host=s" => \$type,
"pass=s"=>\$pass);
$pConfig->{'gpg'} = $gpgpath;
$pConfig->{'type'} = $type;
$pConfig->{'pass'} = $pass;
$pConfig->{'host'} = $host;
print Dumper %$pConfig;
}
Here is the output when I give --gpg='/home/daryl/gpg/pass.gpg to the options in cli :
$VAR1 = 'pass';
$VAR2 = undef;
$VAR3 = 'gpg';
$VAR4 = '/home/daryl/gpg/pass.gpg';
$VAR5 = 'type';
$VAR6 = undef;
$VAR7 = 'host';
$VAR8 = undef;
$VAR1 = {
'pass' => '',
'gpg' => '',
'type' => '',
'output' => '',
'host' => ''
};
How should i proceed ?

If you were to use strict and use warnings, you'd see an error message about using a scalar as a hash reference. That would tip you off that the problem is in this line:
my $pConfig=#_;
You're assigning a scalar context of the array #_ to the variable $pConfig. What this means is that $pConfig is storing the number of elements in the array #_.
Instead, you can do:
my ($pConfig) = #_; as KerrekSB suggests, or:
my $pConfig = shift; (which shifts from #_ automatically)
Take a look at perldoc perldata for more information on calling non-scalars in scalar context. Also, unless you're writing a one-liner or a short throw-away script, make sure to always use strict and use warnings.

Related

Why Dumper output is not evaluated correcty?

I try to eval output of Dumper for pretty simple hashref, where two keys have same value (ref to another hash):
#!/usr/bin/env perl
use strict; use warnings;
use Data::Dumper;
my $foo = { data => 1 };
my $boo = {
x => $foo,
y => $foo,
};
my $VAR1;
my $bar = eval( Dumper( $boo ) );
print Dumper( $boo );
print Dumper( $bar );
I expect the $boo and $bar to have same structure, but eval seems not solve inner-ref $VAR1->{'x'} correctly, I hoped last 2 lines to print same string:
$VAR1 = {
'x' => {
'data' => 1
},
'y' => $VAR1->{'x'}
};
But second has x or y undefined (depending which was referenced in literal form):
$VAR1 = {
'x' => {
'data' => 1
},
'y' => undef
};
I tried simple usage part on doc, and it gave fine results with much more complex structure (no strict, yet), but I can' accomplish it with my data with 2 references to same hash.
What am I missing here?
To correctly capture references inside a structure, you need to set the Purity flag (see the Data::Dumper documentation for details).
$Data::Dumper::Purity = 1;
It's not enough, though, as Dumper($boo) will now return
$VAR1 = {
'y' => {
'data' => 1
},
'x' => {}
};
$VAR1->{'x'} = $VAR1->{'y'};
So, you can't just eval this string, you also need to return $VAR1 from it.
To prevent the purity flag interfering with other parts of the code, you can set it locally:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $foo = { data => 1 };
my $boo = {
x => $foo,
y => $foo,
};
my $VAR1;
my $bar = do {
local $Data::Dumper::Purity = 1;
eval Dumper( $boo );
$VAR1
};
print Dumper( $boo );
print Dumper( $bar );

How to convert query string in hash in perl

I have a query string like this:
id=60087888;jid=16471827;from=advance;action=apply
or it can be like this :
id=60087888&jid=16471827&from=advance&action=apply
Now from this i want to create a hash that will have key as id and its value
I have done this
my %in;
$buffer = 'resid=60087888;jobid=16471827;from=advance;action=apply';
#pairs = split(/=/, $buffer);
foreach $pair (#pairs){
($name, $value) = split(/=/, $pair);
$in{$name} = $value;
}
print %in;
But the issue is in the query string it can be semin colon or & so how can we do this please help me
Don't try to solve it with new code; this is what CPAN modules are for. Specifically in this case, URI::Query
use URI::Query;
use Data::Dumper;
my $q = URI::Query->new( "resid=60087888;jobid=16471827;from=advance;action=apply" );
my %hash = $q->hash;
print Dumper( \%hash );
Gives
{ action => 'apply',
from => 'advance',
jobid => '16471827',
resid => '60087888' }
You've already an answer that works - but personally I might tackle it like this:
my %in = $buffer =~ m/(\w+)=(\w+)/g;
What this does is use regular expressions to pattern match either side of the equals sign.
It does so in pairs - effectively - and as a result is treated by a sequence of key-values in the hash assignment.
Note - it does assume you've not got special characters in your keys/values, and that you have no null values. (Or if you do, they'll be ignored - you can use (\w*) instead if that's the case).
But you get:
$VAR1 = {
'from' => 'advance',
'jid' => '16471827',
'action' => 'apply',
'id' => '60087888'
};
Alternatively:
my %in = map { split /=/ } split ( /[^=\w]/, $buffer );
We split using 'anything that isn't word or equals' to get a sequence, and then split on equals to make the same key-value pairs. Again - certain assumptions are made about valid delimiter/non-delimiter characters.
Check this answer:
my %in;
$buffer = 'resid=60087888;jobid=16471827;from=advance;action=apply';
#pairs = split(/[&,;]/, $buffer);
foreach $pair (#pairs){
($name, $value) = split(/=/, $pair);
$in{$name} = $value;
}
delete $in{resid};
print keys %in;
I know I'm late to the game, but....
#!/usr/bin/perl
use strict;
use CGI;
use Data::Dumper;
my $query = 'id=60087888&jid=16471827&from=advance&action=apply&blank=&not_blank=1';
my $cgi = CGI->new($query);
my %hash = $cgi->Vars();
print Dumper \%hash;
will produce:
$VAR1 = {
'not_blank' => '1',
'jid' => '16471827',
'from' => 'advance',
'blank' => '',
'action' => 'apply',
'id' => '60087888'
};
Which has the added benefit of dealing with keys that might not have values in the source string.
Some of the other examples will produce:
$VAR1 = {
'id' => '60087888',
'1' => undef,
'jid' => '16471827',
'from' => 'advance',
'blank' => 'not_blank',
'action' => 'apply'
};
which may not be desirable.
I would have used URI::Query #LeoNerd 's answer, but I didn't have the ability to install a module in my case and CGI.pm was handy.
also, you could
my $buffer = 'id=60087888&jid=16471827&from=advance&action=apply';
my %hash = split(/&|=/, $buffer);
which gives:
$hash = {
'jid' => '16471827',
'from' => 'advance',
'action' => 'apply',
'id' => '60087888'
};
This is VERY fragile, so I wouldn't advocate using it.

Passing multiple file lists to perl script

I want to pass two file lists to my perl script and have them handled with Getopt::Long for storing an array (via a reference) in a dictionary.
#!/usr/bin/env perl
# author:sb2
use strict;
use warnings;
use Getopt::Long;
use File::Basename;
use Data::Dumper;
print Dumper(#ARGV);
my($config);
$config = &configure(scalar #ARGV);
sub configure{
my $args = shift;
my $config = {};
my #current_samples = ();
#my #old_samples = ();
$config = {'current_samples' => \#current_samples};
#$config = {'old_samples' => \#old_samples};
GetOptions($config,
#"old_samples=s{,}",
"current_samples=s{,}",
"help|h!", )
|| warn "error : $!\n";
print Dumper($config);
return($config);
}
I can happily pass one file list and have it stored as expected:
[sb2 ~]$ perl test.pl -current_samples WS*
$VAR1 = '-current_samples';
$VAR2 = 'WS68726_1401';
$VAR3 = 'WS68726_1402';
$VAR4 = 'WS68726_1500';
$VAR5 = 'WS68726_1501';
$VAR1 = {
'current_samples' => [
'WS68726_1401',
'WS68726_1402',
'WS68726_1500',
'WS68726_1501'
]
};
However, when I uncomment my second list parameter and use that my 'current_samples' variable is now a string with a single filename. Although the 'old_samples' variable has parsed correctly (as above):
[sb2 ~]$ perl test.pl -current_samples WS* -old_samples HG*
$VAR1 = '-current_samples';
$VAR2 = 'WS68726_1401';
$VAR3 = 'WS68726_1402';
$VAR4 = 'WS68726_1500';
$VAR5 = 'WS68726_1501';
$VAR6 = '-old_samples';
$VAR7 = 'HG001';
$VAR8 = 'HG002';
$VAR9 = 'HG003';
$VAR1 = {
'current_samples' => 'WS68726_1501'
'old_samples' => [
'HG001',
'HG002',
'HG003'
]
};
I tried swapping the order of variables around and the only one that made a difference was switching the config assignment ones:
sub configure{
my $args = shift;
my $config = {};
my #current_samples = ();
#my #old_samples = ();
$config = {'current_samples' => \#current_samples};
#$config = {'old_samples' => \#old_samples};
GetOptions($config,
"current_samples=s{,}",
"old_samples=s{,}",
"help|h!", )
|| warn "error : $!\n";
print Dumper($config);
return($config);
}
Produces:
[sb2 ~]$ perl test.pl -current_samples WS* -old_samples HG*
$VAR1 = '-current_samples';
$VAR2 = 'WS68726_1401';
$VAR3 = 'WS68726_1402';
$VAR4 = 'WS68726_1500';
$VAR5 = 'WS68726_1501';
$VAR6 = '-old_samples';
$VAR7 = 'HG001';
$VAR8 = 'HG002';
$VAR9 = 'HG003';
$VAR1 = {
'current_samples' => [
'WS68726_1401',
'WS68726_1402',
'WS68726_1500',
'WS68726_1501'
],
'old_samples' => 'HG003'
};
I can't see anything in the GetOptions CPAN page which alludes to this ordering affect so any help would be greatly appreciated!
From your commented code it looks like you are overwriting $config with these lines:
$config = {'current_samples' => \#current_samples};
#$config = {'old_samples' => \#old_samples};
Instead, do all config assignments in one line:
my $config = {
'current_samples' => \#current_samples,
'old_samples' => \#old_samples,
};
Or you can do them in single lines and assign to the keys of the hashref:
my $config = {};
$config->{'current_samples'} = \#current_samples;
$config->{'old_samples'} = \#old_samples;
As a an alternative solution, Getopt::Declare has syntax to support loading arguments into array references as well:
use strict;
use warnings;
use Getopt::Declare;
my $args = Getopt::Declare->new(
join( "\n",
'[strict]',
"-current-samples <files>... \t List of file names",
"-old-samples <files>... \t List of file names",
)
) || exit(1);
the ... after each tag tells Getopt::Declare to gather arguments into an array reference.
Then you just specify multiple space-separated values on the command line:
perl test-getopt-declare.pl -current-samples a b c d e f -old-samples 1 2 3 4 5 6

How to incorporate hash inside hash in perl?

my %book = (
'name' => 'abc',
'author' => 'monk',
'isbn' => '123-890',
'issn' => '#issn',
);
my %chapter = (
'title' => 'xyz',
'page' => '90',
);
How do I incorporate %book inside %chapter through reference so that when I write "$chapter{name}", it should print 'abc'?
You can copy the keys/values of the %book into the %chapter:
#chapter{keys %book} = values %book;
Or something like
%chapter = (%chapter, %book);
Now you can say $chapter{name}, but changes in %book are not reflected in %chapter.
You can include the %book via reference:
$chapter{book} = \%book;
Now you could say $chapter{book}{name}, and changes do get reflected.
To have an interface that allows you to say $chapter{name} and that does reflect changes, some advanced techniques would have to be used (this is fairly trivial with tie magic), but don't go there unless you really have to.
You could write a subroutine to check a list of hashes for a key. This program demonstrates:
use strict;
use warnings;
my %book = (
name => 'abc',
author => 'monk',
isbn => '123-890',
issn => '#issn',
);
my %chapter = (
title => 'xyz',
page => '90',
);
for my $key (qw/ name title bogus / ) {
print '>> ', access_hash($key, \%book, \%chapter), "\n";
}
sub access_hash {
my $key = shift;
for my $hash (#_) {
return $hash->{$key} if exists $hash->{$key};
}
undef;
}
output
Use of uninitialized value in print at E:\Perl\source\ht.pl line 17.
>> abc
>> xyz
>>

Perl Hash by reference

so I'm trying to write a subroutine that takes a hash parameter and adds a couple key-value pairs to it (by reference). So far, I've got this:
addParams(\%params);
sub addParams
{
my(%params) = %{$_[0]}; #First argument (as a hash)
$params{"test"} = "testing";
}
But for some reason, It doesn't seem to add the 'test' key. I am new to Perl, but isn't this how you pass a hash by reference? Thanks beforehand.
You can use the hash-ref without de-referencing it:
addParams(\%params);
sub addParams
{
my $params = shift;
$params->{"test"} = "testing";
}
EDIT:
To address your code's issue, when you do:
my(%params) = %{$_[0]};
You're actually making a copy of what the ref points to with %{...}. You can see this via a broken down example (no function, same functionality):
my %hash = ( "foo" => "foo" );
my %copy = %{ \%hash };
$hash{"bar"} = "bar";
$copy{"baz"} = "baz";
print Dumper( \%hash );
print Dumper( \%copy );
Run:
$ ./test.pl
$VAR1 = {
'bar' => 'bar',
'foo' => 'foo'
};
$VAR1 = {
'baz' => 'baz',
'foo' => 'foo'
};
Both hashes have the original 'foo => foo', but now each have their different bar/baz's.