puppet define array loop - loops

I am playing with define array but not understand why its not working in following example. I am trying to create following directions
/tmp/foo.0
/tmp/foo.1
/tmp/foo.2
My init.pp
class loop {
define loop ( $x ) {
exec {"$name":
command => "/bin/mkdir /tmp/$name.$x",
creates => "/tmp/$name.$x",
}
}
loop{ "foo": x => ["0", "1", "2"] }
}
Its creating directory like /tmp/tomcat7.012

Not sure this is a good idea to name the define with same name than class.
It would work if you invert the $name and $x
class loop {
define loop::loop ( $x ) {
exec {"$x":
command => "/bin/mkdir /tmp/$x.$name",
creates => "/tmp/$x.$name",
}
}
loop::loop{ ["0", "1", "2"]: x => "foo" }
}
Hope this helps

Related

Perl JSON arrays within a parent key

I have created a script to get some information from various external sources, the results should then be in json format. There is a lot of data and I push everything to an array in a loop, then print the json array after everything has been completed, an extract of that loop part of the script:
#!/usr/bin/perl
use JSON -convert_blessed_universally;
use strict;
use warnings;
my #json_arr;
my #servers = ("SERVER1", "SERVER2");
my #details = ("SERVER1,10.1.2.3,Suse Linux",
"SERVER2,10.1.2.4,Windows 10",
"SERVER3,10.1.2.5,Windows XP");
my $json = JSON->new->convert_blessed;
foreach my $server(#servers) {
foreach (#details) {
my #detail = split(',',$_);
if ($server eq $detail[0]) {
push #json_arr, {name => "$server", ip => "$detail[1]", os => "$detail[2]"};
}
}
}
my $result = $json->encode(\#json_arr);
print $result;
This gives an output of:
[
{
"name" : "SERVER1",
"ip" : "10.1.2.3",
"os" : "Suse Linux",
},
{
"name" : "SERVER2",
"ip" : "10.1.2.4",
"os" : "Widows 10"
}
]
and a screenshot:
I am however trying to do it by setting a key element instead and having the additional data as children of the device name, i.e:
{
"instance" : [
{
"SERVER1" : {
"ip" : "10.1.2.3",
"os" : "Suse Linux"
},
"SERVER2" : {
"ip" : "10.1.2.4",
"os" : "Windows 10"
}
}
]
}
So I have tried a few things, including something like the below, then pushing to array, but I am getting funny results and just not getting the desired results.
my $json = '{
"instance" : [
$server => {
ip => "$detail[0]",
os => "$detail[1]"
}
]
}';
push #json_arr, $json;
It only takes a small re-arrangement
use warnings;
use strict;
use feature 'say';
use Data::Dumper;
use JSON::XS;
...
my #json_ary;
foreach my $server (#servers) {
foreach (#details) {
my #detail = split /,/;
if ($server eq $detail[0]) {
#push #json_ary, {name => "$server", ip => "$detail[1]" ...
push #json_ary,
{ $server => { ip => $detail[1], os => $detail[2] } }
}
}
}
print Dumper \#json_ary;
# Encode `{ instance => \#json_ary }` for the desired output
my $json = JSON->new->convert_blessed;
my $result = $json->pretty->encode( { instance => \#json_ary } );
say $result;
A few notes
No need for quotes around variables, since they get evaluated anyway ("$detail[0]" --> $detail[0] etc)
No need for quotes around hash keys, as a syntax convenience: key => 'value' is OK (and if the value is a variable it's just key => $var). That is, as long as there are no spaces in the key name
One way to pretty-print an existing JSON string:
print JSON::XS->new->ascii->pretty->encode(decode_json $json_str);
There may be a question of whether a hashref around each server entry (JSON objects) is needed or not; the above was confirmed in a comment after a discussion so I settled with it.
But if the output only needs a hashref with entries for servers in the array (and not an array of hashrefs for each server) then the code in the question can be modified to
my %server_details;
foreach my $server (#servers) {
foreach (#details) {
my #detail = split /,/;
if ($server eq $detail[0]) {
$server_details{$server} = { ip => $detail[1], os => $detail[2] }
}
}
}
Now this hash has details for all servers, and can be encoded with the key instance. Then it is not clear to me what the overall structure should be; one option is
my $result = JSON->pretty->encode( { instance => [ \%server_details ] } );
This would be suitable if there is more data in reality. If not then there may not be any need for an arrayref, but encode the usual
{ instance => \%server_details }
The problem is that you are adding hashes to an array (push #json_arr, ...) when you mean to add to a hash ($instance{ $server_name } = ...).
my %servers = map { $_ => 1 } #servers;
my %instance;
for ( #details ) {
my ( $name, $ip, $os ) = split /,/;
next if !$servers{ $name };
$instance{ $name } = {
ip => $ip,
os => $os,
};
}
my #instances = \%instance;
my $data = { instance => \#instances };
Or using map:
my %servers = map { $_ => 1 } #servers;
my %instance = (
map { $_->[0] => { ip => $_->[1], os => $_->[2] } }
grep $servers{ $_->[0] },
map [ split /,/ ],
#details
);
my #instances = \%instance;
my $data = { instance => \#instances };
This produces the following, as requested:
{
"instance" : [
{
"SERVER1" : {
"ip" : "10.1.2.3",
"os" : "Suse Linux"
},
"SERVER2" : {
"ip" : "10.1.2.4",
"os" : "Windows 10"
}
}
]
}
(This is different than zdim's first solution.)
Note that I got rid of the nested loops because they are nasty. For a few items, it's not a problem. But performance would suffer is #servers or #details would become large. So it's a bad idea, and a bad habit to get into.
Having an array which only even has a single element is weird. Did you perhaps want
{
"instance" : {
"SERVER1" : {
"ip" : "10.1.2.3",
"os" : "Suse Linux"
},
"SERVER2" : {
"ip" : "10.1.2.4",
"os" : "Windows 10"
}
}
}
This would be achieved by replacing
my $data = { instance => \#instances };
with
my $data = { instance => \%instance };

Dereferencing an array reference from a nested Perl hash

I hope I've stated that subject correctly. I have a hash of hashes that I've built from reading a file. The outer hash is groups, then the inner hash is parameters within that group. Each parameter value can either be a scalar or array, and the arrays can start at either zero or one.
I've written a subroutine that returns the value of a parameter. The calling function has to figure out whether the returned value is a scalar or an array. Works fine for scalars. Returns a reference to an array for array values (looks like ARRAY(0x004f00)). Using Data::Dumper spits out data that looks like an array, but I can't figure out how to dereference it in the code. Can someone point to what I'm doing wrong?
%HoH = (
flintstones => {
husband => "fred",
possessions => [ undef, "car", "record player", "rock" ],
pal => "barney",
pets => [ "bird", "dinosaur" ],
},
);
get_value("possessions");
sub get_value {
my $what_i_want = shift;
#groups = keys(%HoH);
foreach my $group ( #groups ) {
foreach my $param ( keys( %{ HoH {group} } ) ) {
if ( $param eq $what_i_want ) {
return $HoH{$group}{$param};
}
}
}
}
The caller assigns the return value to an array, #return, so in the case of a scalar it should put the value in $return[0].
In the case of an array, it should populate the array. When I call Dumper, it prints out scalars in single quotes and arrays in square brackets, as it should. However, when I use scalar(#return) to check the size of the array, it returns 1.
I've tried dereferencing the return statement using square brackets at the end to see if I could even get a scalar returned, but no luck.
You don't show the subroutine being called in context, but a quick fix would be to put this after the call
#return = #{ $return[0] } if ref $return[0]
Update
You're missing the point of hashes. You can access the correct element of the parameter hash by using $what_i_want as a hash key
I suggest you change your subroutine code to look like this
for my $group ( keys %HoH ) {
my $ret = $HoH{$group}{$what_i_want};
return unless defined $ret;
return ref $ret ? #$ret : $ret;
}
That way it will never return a reference
Update 2
Here's your complete program modified as I suggested
use strict;
use warnings 'all';
my %HoH = (
flintstones => {
husband => "fred",
possessions => [ undef, "car", "record player", "rock" ],
pal => "barney",
pets => [ "bird", "dinosaur" ],
},
);
my #return = get_value('possessions');
use Data::Dump;
dd \#return;
sub get_value {
my ($wanted) = #_;
for my $group ( keys %HoH ) {
my $ret = $HoH{$group}{$wanted};
if ( defined $ret ) {
return ref $ret ? #$ret : $ret;
}
}
return;
}
output
[undef, "car", "record player", "rock"]

Perl: Getting difference between two arrays of hashes?

I have two array references that contain hashes:
$A = [
{
"t" => "1419054300000",
"v" => "28.1"
},
{
"t" => "1419053400000",
"v" => "28.2"
},
{
"t" => "1419052500000",
"v" => "28.4"
}
];
$B = [
{
"t" => "1419053400000",
"v" => "28.2"
},
{
"t" => "1419052500000",
"v" => "28.4"
}
];
I want to get only the hashes from $A where their value of t doesn't already exist in one of the hashes in $B (the t values are unique per arrayref, v isn't).
I assume there's some obvious method of doing this, but I've been banging my head against this all day without success.
You can use the perl5i diff method.
use perl5i::2;
...initialize $A and $B...
say $A->diff($B)->mo->as_perl;
__END__
[
{
't' => '1419054300000',
'v' => '28.1'
}
]
As always you can build hash look up where keys are elements you want to filter out,
my %seen;
#seen{ map $_->{t}, #$B } = ();
my $C = [
grep { !exists $seen{$_->{t}} } #$A
];

references in perl: hash of array to another array

I have a problem with referencing a hash in an array to another array.
I have an array #result which looks like this:
#result = (
{ "type" => "variable",
"s" => "NGDP",
"variable" => "NGDP" },
{"type" => "subject",
"s" => "USA",
"subject" => "USA",
"variable" => "NGDP" },
{ "type" => "colon",
"s" => ",",
"colon" => "," },
{ "type" => "subject",
"s" => "JPN",
"subject" => "JPN",
"variable" => "NGDP" },
{ "type" => "operator",
"s" => "+",
"operator => "+" },
{"type" => "subject",
"s" => "CHN",
"subject" => "CHN",
"variable" => "NGDP" },
);
I want to divide this array into colons and push elements of the #result array to another array, so i wrote the script:
for ($i = 0; $i <= $#result; $i++) {
if (defined $result[$i]{subject} or $result[$i]{operator} and not defined $result[$i]{colon}) {
push #part_col, \%{$result[$i]};
}
elsif ($i == $#result) {
push #part_col_all, \#part_col;
}
elsif (defined $result[$i]{colon}) {
push #part_col_all, \#part_col;
my #part_col;
}
}
So what I need is that if I print out $part_col_all[0][0]{subject} the result will be "USA",
and for $part_col_all[1][0]{subject} will be "JPN",
and for $part_col_all[1][1]{operator} will be "+" etc.
My result for $part_col_all[0][0]{subject} is "USA"
and for $part_col_all[0][1]{subject} is "JPN" which should be in $part_col_all[1][0]{subject}.
The result for $part_col_all[0][3]{subject} is "CHN", while it should be in $part_col_all[1][2]{subject}.
I'm making an application which is creating graphs from economical data based on a certain economical input. The #result array is my preprocessed input where I know to which country which variable belongs. If I get an input like GDP USA CAN, JPN+CHN I need to split this input to GDP USA CAN and JPN+CHN. That's why I made a condition, if colon is found, push everything in #part_col to the first element of #part_col_all, and then if it's on the end of the input, push JPN+CHN to the second element of #push_col_all.
So #part_col_all should looks like this:
#part_col_all = (
(
{"type" => "subject",
"s" => "USA",
"subject" => "USA",
"variable" => "NGDP" },
{"type" => "subject",
"s" => "CAN",
"subject" => "CAN",
"variable" => "NGDP" },
),
(
{ "type" => "subject",
"s" => "JPN",
"subject" => "JPN",
"variable" => "NGDP" },
{ "type" => "operator",
"s" => "+",
"operator" => "+" },
{"type" => "subject",
"s" => "CHN",
"subject" => "CHN",
"variable" => "NGDP" },
)
);
I dont know what I'm doing wrong. Sorry if there are any basic mistakes, im a beginner. Thanks a lot.
First, you're missing a quote:
{ "type" => "operator",
"s" => "+",
"operator" => "+" },
^ missing
As for printing, you can do the following:
foreach my $part (#part_col){
print $part->{operator}."\n";
}
Or do whatever you want in the print cycle with the values
You should read the Perl Reference Tutorial to help you.
There's no sin in dereferencing to simplify your code:
my #part_col;
my #part_col_all;
for $i ( 0..$#array ) {
my %hash = ${ $result[$i] }; # Make it easy on yourself. Dereference
if ( defined $hash{subject} or defined $hash{operator} and not defined $hash{colon} ) {
push #part_col, \%hash; # or push, #par_col, $result[$i]
}
}
Notice I changed the for from the three part setup you had to a cleaner and easier to understand way of stating it.
Looking closer at your data structure, I notice that $hash{type} will tell you whether or not $hash{operator}, $hash{subject}, or $hash{colon} is defined. Let's just use $hash{type} and simplify that if:
my #part_col;
my #part_col_all;
for my $i ( 0..$#array ) {
my %hash = ${ $result[$i] }; # Make it easy on yourself. Dereference
if ( $hash{type} eq "subject" or $hash{type} eq "operator" ) {
push #part_col, \%hash; # or push, #par_col, $result[$i]
}
}
In fact, since #array is just an array, I'll treat it like one. I'll use a simple for structure to go through each element of my array. Each element is a hash_reference, so:
for my $hash_ref ( #array ) {
my %hash = %{ %hash_ref };
if ( $hash{type} eq "subject" or $hash{type} eq "operator" ) {
push #part_col, \%hash;
}
}
And further simplification, I can dereference and talk about a particular element of my hash all at once by using the -> syntax:
for my $hash_ref ( #array ) {
if ( $hash_ref->{type} eq "subject" or $hash_ref->{type} eq "operator" ) {
push #part_col, $hash_ref;
}
}
I'm trying to understand the rest of your code:
elsif ($i == $#result) {
push #part_col_all, \#part_col;
}
elsif (defined $hash_ref->{colon}) {
push #part_col_all, \#part_col;
my #part_col;
}
}
These pushes of #part_col onto #part_col_all confuse me. Exactly what are you trying to store in #part_col_all? Remember that \#part_col is the location in memory where you're storing #part_col. You're pushing that same memory location over and over onto that hash, so you're storing the same reference over and over again. Is that really what you want? I doubt it.
You need to do is to decide exactly what your data structure really represents. A data structure should have a solid definition. What does the data structure #part_col_all represent? What does the data structure $part_col_all[$i] represent? What does the data structure $part_col_all[$i]->[$j] represent? Without knowing this, it's very hard to answer the rest of your question.
Are you storing elements where the type is colon in one array and everything else in another array? Or are you storing everything in one array, and in another array, storing everything that's not a type colon?
Once I understand this, I can answer the rest of your question.
Addendum
Thank you for your reply, I will try that way and write my results. It is realy helpful. I updated my question with more information about data structure of #part_col_all. I hope that you understand what I'm trying to explain, if not I'll try it again.
If I understand what you're doing, someone enters in NGDP USA , JPN+CNA and that means you're comparing the NGDP between the United States vs. Japan and China combined.
It seems to me that you would want three separate variables:
$parameter - What you are measuring. (GDP, etc.)
#countries_set_1 - The first set of countries
#countries_set_2 - The second set of countries which you're comparing against the first set.
And, what you call the colon (which we would call a comma in the U.S.) as a separator between the first set of countries vs. the second set. Then, you'd simply go through a loop. It could be that the two arrays are merely two elements of the same array, and the sets of countries are array references. I imagine something like this:
#input = qw(GDP USA, JPN CHN); # Compare the GDP of the USA with Japan and China together
my $parameter = shift #input; # Remove what you're measuring
my #country_sets; # An array of arrays
my $set = 0 # Which set you're on
for my $value ( #input ) {
if ( $value eq "," ) {
$set += 1; # Next Set
next;
}
push #{ $country_sets[$set] }, $input;
}
This would create a data structure like this:
#country_sets = (
(
USA,
),
(
JPN,
CHN,
),
)
No need for the complex #results since you're only going to have a single operation (GDP, etc.) for all involved.
However, I think I see what you want. We'll go with an array of arrays. Here's what I had before:
for my $hash_ref ( #array ) {
if ( $hash_ref->{type} eq "subject" or $hash_ref->{type} eq "operator" ) {
push #part_col, $hash_ref;
}
}
We'll combine that and the code I offered right above which splits the countries into two sets:
my #country_sets; # An array of arrays
my $set = 0 # Which set you're on
for my $country_ref ( #array ) {
next if $country_ref->{type} eq "variable"; # We don't want variables
if ( $country_ref{type} eq "colon" ) { # Switch to the other country set
set += 1;
next;
}
push #{ $country_sets[$set] }, $country_ref;
}
The first few entries will go into $country_sets[0] which will be an array reference. After the colon (which won't be input into the set), the second set of countries will go into $country_sets[1] which will be an other array_ref to a reference of hashes:
#country_sets - Contains the input information into two sets
#country_sets[$x] - A particular set of countries (and possibly operator)
#country_sets[$x]->[$y] - A Particular country or operator
#country_sets[$x]->[$y]->{$key} - A particular value from a particular country
Where $x goes from 0 to 1. This will give you something like this:
$country_sets[0] = (
{
"type" => "subject",
"s" => "USA",
"subject" => "USA",
"variable" => "NGDP",
},
)
$country_sets[1] = (
{
"type" => "subject",
"s" => "JPN",
"subject" => "JPN",
"variable" => "NGDP",
},
{
"type" => "operator",
"s" => "+",
"operator => "+",
},
{
"type" => "subject",
"s" => "CHN",
"subject" => "CHN",
"variable" => "NGDP",
},
);

Perl: Get parallel value in hash array

I have this:
my(%arr) = (
monsters => ["Test","Test2"],
kills => [-1, -2 ]);
Then later I search for Test2:
if ( grep { $_ eq "Test2"} #{ $arr{monsters} } )
{
#Get parallel value of Test2 (-2)
next;
}
How can I get the parallel value without knowing the index (an actual variable is used when searching and not a string literal)?
Rather than using a grep, just loop over the array and keep a count variable:
for my $idx( 0 .. $#{ $arr{monsters} } ) {
if ( $arr{monsters}[$idx] eq 'Test2' ) {
print "Kills = $arr{kills}[$idx]\n";
last;
}
}
A better way to handle this, however, might be to rethink your data structure. Instead of parallel arrays, consider an array of hashes:
my #monsters = ( { name => 'Test', kills => -1 }, { name => 'Test2', kills => -2 } );
Now, to find a specific monster:
my ( $monst ) = grep { $_->{name} eq 'Test2' } #monsters;
print $monst->{kills};
This would allow you to search by name and kills equally easily. If you are going to always search by name, then making a hash keyed on name and pointing to the number of kills (as #dmah suggests) might be better.
An even better way to handle this would be to wrap up your monsters in a class, and have each object keep track of its own kills, but I'll leave that as an exercise for the OP.
Try a hash of hashes:
my %arr = (
'Test' => {
'kills' => -1,
},
'Test2' => {
'kills' => -2,
},
);
print $arr{'Test2'}{'kills'}, "\n";

Resources