I have the following example exhibiting the problem I'm struggling to resolve.
In the toy example, I have an array #actors with two levels.
I also have an array of hashes #people which I am using to 'look up' properties of the people in #actors.
The output of the program should be:
blue, blue cat, cat
red, red dog, dog
blue, blue cat, cat
red, red dog, dog
but instead I'm getting:
blue, cat cat, cat
red, dog dog, dog
blue, cat cat, cat
red, dog dog, dog
That is, it seems that in setting $favanim[$i][$j] I seem to be also overwriting the value of $favcols[$i][$j].
I suspect that for some reason the fact that #actors is a 2-dimensional array means that assignments via = are as references rather than as values, though I don't know why or how to stop it.
Please help!
The toy program is here: (I apologise if it can be simplified whilst still exhibiting the problem - it has taken me most of the afternoon to strip it down to this)
#!/usr/bin/perl -w
my #people = ();
$people[0]{'alternative full names for regexp'} = 'matthew smith|matt smith';
$people[1]{'alternative full names for regexp'} = 'david tennant|dave tennant';
$people[0]{'fav colour'} = 'red';
$people[1]{'fav colour'} = 'blue';
$people[0]{'fav animal'} = 'dog';
$people[1]{'fav animal'} = 'cat';
my #actors = ();
$actors[0][0] = 'David Tennant';
$actors[0][1] = 'Matt Smith';
$actors[1][0] = 'David Tennant';
$actors[1][1] = 'Matt Smith';
my #favcols = #actors;
my #favanim = #actors;
for ($i=0; $i<2; $i++) {
for ($j=0; $j<2; $j++) {
my #matching_people = grep{$actors[$i][$j] =~ m/^$_->{'alternative full names for regexp'}$/i} #people;
$favcols[$i][$j] = $matching_people[0]{'fav colour'};
$favanim[$i][$j] = $matching_people[0]{'fav animal'};
print "$matching_people[0]{'fav colour'}, $favcols[$i][$j] $matching_people[0]{'fav animal'}, $favanim[$i][$j]\n";
}
}
Try using
#favcols = map { [#$_] } #actors;
#favanim = map { [#$_] } #actors;
Deep copy vs shallow copy.
The problem is that you are initialising #favcols and #favanim by copying the contents of #people, which contains two array references
That sets $favcol[0] and $favanim[0] to a reference to the same array, [ 'David Tennant', 'Matt Smith' ], so when you modify $favcols[$i][$j] and then $favanim[$i][$j] you are overwriting the same array element
I don't see any reason to initialise your arrays at all, and if you declare them as just
my (#favcols, #favanim);
then you will find that your program does what you expect
By the way, you must always use strict, and use warnings is preferable to -w on the command line
Related
I am looking to provide users with a size list that also compares against a threshold. if threshold is exceeded, that should generate a RED X mark. otherwise, if threshold is not exceeded that should generate a green checkmark
i found this code:
$greenCheck = #{
Object = [Char]8730
ForegroundColor = 'Green'
NoNewLine = $true
}
Write-Host #greenCheck
that is fantastic but requires write-host...i cant just call it like this in other words #greenCheck
The splatting operator '#' cannot be used to reference variables in
an expression. '#greenCheck' can be used only as an argument to a
command. To reference variables in an expression use '$greenCheck'.
i found this other code here
$symbols = [PSCustomObject] #{
SMILEY_WHITE = ([char]9786)
SMILEY_BLACK = ([char]9787)
GEAR = ([char]9788)
HEART = ([char]9829)
DIAMOND = ([char]9830)
CLUB = ([char]9827)
SPADE = ([char]9824)
CIRCLE = ([char]8226)
NOTE1 = ([char]9834)
NOTE2 = ([char]9835)
MALE = ([char]9794)
FEMALE = ([char]9792)
YEN = ([char]165)
COPYRIGHT = ([char]169)
PI = ([char]960)
TRADEMARK = ([char]8482)
CHECKMARK = ([char]8730)
}
this one is perfect! but it doesnt have colors. how can i add colors to the object?
also, is there more symbols i can find like an X mark? or i would have to just use good ol' 'X' char?
For a green checkmark and a red x try: "$([char]0x1b)[92m$([char]8730) $([char]0x1b)[91m×"
Some Links:
❌ https://emojipedia.org/microsoft/windows-10-october-2018-update/cross-mark/
How can I get Mocha's Unicode output to display properly in a Windows console?
Ansi escape codes
The new Terminal allows change of the font size like other apps with CTRL+Wheel among other goodies.
I am having a little bit of trouble with hashtables/dictionaries in powershell. The most recent roadblock is the ability to find the index of a key in an ordered dictionary.
I am looking for a solution that isn't simply iterating through the object.
(I already know how to do that)
Consider the following example:
$dictionary = [Ordered]#{
'a' = 'blue';
'b'='green';
'c'='red'
}
If this were a normal array I'd be able to look up the index of an entry by using IndexOf().
[array]::IndexOf($dictionary,'c').
That would return 2 under normal circumstances.
If I try that with an ordered dictionary, though, I get -1.
Any solutions?
Edit:
In case anyone reading over this is wondering what I'm talking about. What I was trying to use this for was to create an object to normalize property entries in a way that also has a numerical order.
I was trying to use this for the status of a process, for example:
$_processState = [Ordered]#{
'error' = 'error'
'none' = 'none'
'started' = 'started'
'paused' = 'paused'
'cleanup' = 'cleanup'
'complete' = 'complete'
}
If you were able to easily do this, the above object would give $_processState.error an index value of 0 and ascend through each entry, finally giving $_processState.complete an index value of 5. Then if you compared two properties, by "index value", you could see which one is further along by simple operators. For instance:
$thisObject.Status = $_processState.complete
If ($thisObject.Status -ge $_processState.cleanup) {Write-Host 'All done!'}
PS > All done!
^^that doesn't work as is, but that's the idea. It's what I was aiming for. Or maybe to find something like $_processState.complete.IndexNumber()
Having an object like this also lets you assign values by the index name, itself, while standardizing the options...
$thisObject.Status = $_processState.paused
$thisObject.Status
PS > paused
Not really sure this was the best approach at the time or if it still is the best approach with all the custom class options there are available in PS v5.
It can be simpler
It may not be any more efficient than the answer from Frode F., but perhaps more concise (inline) would be simply putting the hash table's keys collection in a sub expression ($()) then calling indexOf on the result.
For your hash table...
Your particular expression would be simply:
$($dictionary.keys).indexOf('c')
...which gives the value 2 as you expected. This also works just as well on a regular hashtable... unless the hashtable is modified in pretty much any way, of course... so it's probably not very useful in that case.
In other words
Using this hash table (which also shows many of the ways to encode 4...):
$hashtable = [ordered]#{
sample = 'hash table'
0 = 'hello'
1 = 'goodbye'
[char]'4' = 'the ansi character 4 (code 52)'
[char]4 = 'the ansi character code 4'
[int]4 = 'the integer 4'
'4' = 'a string containing only the character 4'
5 = "nothing of importance"
}
would yield the following expression/results pairs:
# Expression Result
#------------------------------------- -------------
$($hashtable.keys).indexof('5') -1
$($hashtable.keys).indexof(5) 7
$($hashtable.keys).indexof('4') 6
$($hashtable.keys).indexof([char]4) 4
$($hashtable.keys).indexof([int]4) 5
$($hashtable.keys).indexof([char]'4') 3
$($hashtable.keys).indexof([int][char]'4') -1
$($hashtable.keys).indexof('sample') 0
by the way:
[int][char]'4' equals [int]52
[char]'4' has a "value" (magnitude?) of 52, but is a character, so it's used as such
...gotta love the typing system, which, while flexible, can get really really bad at times, if you're not careful.
Dictionaries uses keys and not indexes. OrderedDictionary combines a hashtable and ArrayList to give you order/index-support in a dictionary, however it's still a dictionary (key-based) collection.
If you need to get the index of an object in a OrderedDictionary (or a hasthable) you need to use foreach-loop and a counter. Example (should be created as a function):
$hashTable = [Ordered]#{
'a' = 'blue';
'b'='green';
'c'='red'
}
$i = 0
foreach($key in $hashTable.Keys) {
if($key -eq "c") { $i; break }
else { $i++ }
}
That's how it works internaly too. You can verify this by reading the source code for OrderedDictionary's IndexOfKey method in .NET Reference Source
For the initial problem I was attempting to solve, a comparable process state, you can now use Enumerations starting with PowerShell v5.
You use the Enum keyword, set the Enumerators by name, and give them an integer value. The value can be anything, but I'm using ascending values starting with 0 in this example:
Enum _ProcessState{
Error = 0
None = 1
Started = 2
Paused = 3
Cleanup = 4
Complete = 5
Verified = 6
}
#the leading _ for the Enum is just cosmetic & not required
Once you've created the Enum, you can assign it to variables. The contents of the variable will return the text name of the Enum, and you can compare them as if they were integers.
$Item1_State = [_ProcessState]::Started
$Item2_State = [_ProcessState]::Cleanup
#return state of second variable
$Item2_state
#comparison
$Item1_State -gt $Item2_State
Will return:
Cleanup
False
If you wanted to compare and return the highest:
#sort the two objects, then return the first result (should return the item with the largest enum int)
$results = ($Item1_State,$Item2_State | Sort-Object -Descending)
$results[0]
Fun fact, you can also use arithmetic on them, for example:
$Item1_State + 1
$Item1_State + $Item2_State
Will return:
Paused
Verified
More info on Enum here:
https://blogs.technet.microsoft.com/heyscriptingguy/2015/08/26/new-powershell-5-feature-enumerations/
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_enum?view=powershell-6
https://psdevopsug.scot/post/working-with-enums-in-powershell/
I think I've got the gist of creating a table using Perl's PDF::Report and PDF::Report::Table, but am having difficulty seeing what the 2-dimensional array #data would look like.
The documentation says it's a 2-dimensional array, but the example on CPAN just shows an array of arrays test1, test2, and so on, rather than the example showing data and formatting like $padding $bgcolor_odd, and so on.
Here's what I've done so far:
$main_rpt_path = "/home/ics/work/rpts/interim/mtr_prebill.rpt";
$main_rpt_pdf =
new PDF::Report('PageSize' => 'letter', 'PageOrientation' => 'Landscape',);
$main_rpt_tbl_wrt =
PDF::Report::Table->new($main_rpt_pdf);
Obviously, I can't pass a one dimensional array, but I have searched for examples and can only find the one in CPAN search.
Edit:
Here is how I am trying to call addTable:
$main_rpt_tbl_wrt->addTable(build_table_writer_array($pt_column_headers_ref, undef));
.
.
.
sub build_table_writer_array
# $data -- an array ref of data
# $format -- an array ref of formatting
#
# returns an array ref of a 2d array.
#
{
my ($data, $format) = #_;
my $out_data_table = undef;
my #format_array = (10, 10, 0xFFFFFF, 0xFFFFCC);
$out_data_table = [[#$data],];
return $out_data_table;
}
and here is the error I'm getting.
Use of uninitialized value in subtraction (-) at /usr/local/share/perl5/PDF/Report/Table.pm line 88.
at /usr/local/share/perl5/PDF/Report/Table.pm line 88
I cannot figure out what addTable wants for data. That is I am wondering where the formatting is supposed to go.
Edit:
It appears the addData call should look like
$main_rpt_tbl_wrt->addTable(build_table_writer_array($pt_column_headers_ref), 10,10,xFFFFFF, 0xFFFFCC);
not the way I've indicated.
This looks like a bug in the module. I tried running the example code in the SYNOPSIS, and I got the same error you get. The module has no real tests, so it is no surprise that there would be bugs. You can report it on CPAN.
The POD has bugs, too.
You increase your chances of getting it fixed if you look at the source code and fix it yourself with a patch.
Here is my naive approach:
# puppet/init.pp
$x = 'hello ' +
'goodbye'
This does not work. How does one concatenate strings in Puppet?
Keyword variable interpolation:
$value = "${one}${two}"
Source: http://docs.puppetlabs.com/puppet/4.3/reference/lang_variables.html#interpolation
Note that although it might work without the curly braces, you should always use them.
I use the construct where I put the values into an array an then 'join' them.
In this example my input is an array and after those have been joined with the ':2181,' the resulting value is again put into an array that is joined with an empty string as separator.
$zookeeperservers = [ 'node1.example.com', 'node2.example.com', 'node3.example.com' ]
$mesosZK = join([ "zk://" , join($zookeeperservers,':2181,') ,":2181/mesos" ],'')
resulting value of $mesosZK
zk://node1.example.com:2181,node2.example.com:2181,node3.example.com:2181/mesos
Another option not mentioned in other answers is using Puppet's sprintf() function, which functions identically to the Ruby function behind it. An example:
$x = sprintf('hello user %s', 'CoolUser')
Verified to work perfectly with puppet. As mentioned by chutz, this approach can also help you concatenate the output of functions.
The following worked for me.
puppet apply -e ' $y = "Hello" $z = "world" $x = "$y $z" notify { "$x": } '
notice: Hello world
notice: /Stage[main]//Notify[Hello world]/message: defined 'message' as 'Hello world'
notice: Finished catalog run in 0.04 seconds
The following works as well:
$abc = "def"
file { "/tmp/$abc":
You could use the join() function from puppetlabs-stdlib. I was thinking there should be a string concat function there, but I don't see it. It'd be easy to write one.
As stated in docs, you can just use ${varname} interpolation. And that works with function calls as well:
$mesosZK = "zk://${join($zookeeperservers,':2181,')}:2181/mesos"
$x = "${dirname($file)}/anotherfile"
Could not use {} with function arguments though: got Syntax error at '}'.
I'm googling since a while, but I didn't find a solution to my problem.
I have to say I'm newbie in Powershell.
I would like to create the following array
$a = (A,B,C,D) where
A = 1 string (always)
B = 1 string (always)
C = undefined number of strings. I need to be able to add elements dynamically
D = undefined number of strings. I need to be able to add elements dynamically (same number as C)
Is this possible?
Example of 2 elements of the array
("WSTM0123456", "192.168.10.155",("WSTM8765421","WSTM9856454","WSTM1289765"),("192.36.36.36", "187.25.25.25","192.69.89.65"))
("WLDN1251254", "156.25.36.54", ("WLDN1234512", "WLDN9865323"), ("187.154.12.12","163.136.25.98"))
I don't know a priori how many elements will be in C and D and I'll have to append strings in position C and D with a for cycle.
Scope: group many strings (C & D) under the same string (A/B) which are in common.
Any help would be appreciated
Thanks,
Marco
You can do this, but it's probably quite painful as dealing with arrays is sometimes cumbersome in PowerShell due to lots of implicit flattening.
I'd suggest creating a custom type for this. Then you can also give the individual parts useful names (I don't know the purpose of what you're doing here, so I'm making up names here. Feel free to change):
$properties = #{
Name = 'WSTM0123456';
IP = [ipaddress]'192.168.10.155';
ListOfNames = #("WSTM8765421","WSTM9856454","WSTM1289765");
ListOfIPs = [ipaddress[]]#("192.36.36.36", "187.25.25.25","192.69.89.65")
}
$foo = New-Object PSObject -Property #properties
Then you can simply append new items like so:
$foo.ListOfNames += 'AnotherName'
I think this is pretty much the same idea. Use a hash table, and make two of the elements arrays. This is how you would create the arrays "on the fly" at runtime, without knowing what any of the contents were going to be in advance, taking $x and putting any item that starts with "t" in "C" , and everything else in "D":
$a = #{A = "Some string";B = "Some other string"}
$x = "one","two","three","four","five"
$x |% {
if ($_ -match "^t"){$a["C"] += #($_)}
else {$a["D"] += #($_)}
}
$a.a
Some string
$a.b
Some other string
$a.c
two
three
$a.d
one
four
five
$obj = new-object psobject -property $a