Why does my PowerShell script prompt for permission to add items to arrays? - arrays

I have a PowerShell script with the following excerpt:
foreach ($pc in $ComputerName) {
$appnames = $appnames | Sort-Object
Write-Debug "Number of entries in `$appnames = $($appnames.count)"
if ($AsHTML) {#Switch Parameter
Write-Verbose "Generating HTML Report..."
$th = "<TR><TH>Application Name</TH>" #Create Top header
foreach ($pc in $ComputerName) {
$th += "<TH>$pc</TH>" #Another header for each pc
}
$th += "</TR>" #Header finished
$rows = ""
foreach ($app in $appnames) {
$rows += "<TR><TH>$app</TH>"
foreach ($pc in $ComputerName) {
Write-Debug $RegistryEntries[$pc].Value[$app]
$currentApp = $RegistryEntries[$pc].Value[$app]
if ($currentApp) {
if ($currentApp.DisplayVersion) {
$status = $currentApp.DisplayVersion
} else {
$status = "Version-nr. N/A"
}
} else {
$status = "Application N/A"
}
$rows += "<TD>$status</TD>" #Intersection cell for each pc
}
$rows += "</TR>"
}
Write-Verbose "Finishing html report..."
$html = "
<html>
<head>
<style>
body { background-color:#FFFFCC;
font-family:Tahoma;
font-size:12pt; }
td, th { border:1px solid #000033;
border-collapse:collapse; }
th { color:white;
background-color:#000033; }
table, tr, td, th { padding: 0px; margin: 0px }
table { margin-left:10px; }
</style>
<Title>Application versions Report</Title>
</head>
<body>
<table>
$th
$rows
</table>
</body>
</html>"
}
}
So, to explain the wall of text above a bit;
$RegistryEntries is a Hashtable of Hashtables, with the top-level keys being computer names, and the low-level hashtable keys being application names found in the Uninstall part of the registry. The corresponding values to the Application-name-keys are custom PSObjects with three general properties: .Displayname, .DisplayVersion, and .UninstallString. (Not all objects have all of the three properties, but each object has at least one).
What I hope to achieve with this HTML-table is to get some kind of "pivot-table" (ref. Wikipedia entry for Pivot Tables, but not quite), where I can get Application Names on the Y-axis, and computer names on the X-axis, and the Version number of said application on said computer where they intersect.
So again, with that in mind, could someone help me understand why my script when run prompts me in the shell for permission to add application names to the array $appnames (elsewhere in the script), as well as doing the same with the HTML input that is being put into $rows?
Another thing which is a bit on the side (maybe even off-topic), my $RegistryEntries object, the hashtable of hashtables, is for some reason not possible to access in the way I do it on the two following lines:
Write-Debug $RegistryEntries[$pc].Value[$app]
$currentApp = $RegistryEntries[$pc].Value[$app]
Would anyone be able to tell me why?
To sum up/TL;DR:
Why does my function when trying to add items to an array created inside the script prompt me for permission to do just this in the shell?
With the custom object I've described above holding the data I want to display in my HTML table, what am I doing wrong in trying to access it in the above code excerpt?
PS: The script works in the sense that if I sit throughout all the prompts I get in the shell, hitting A + Return all the time, I will get a HTML table of the kind that I want, but all the cells where an application name interstices a computername will say "Application N/A".

I guess you have your $DebugPreference and/or VerbosePreference set to Inquire which will prompt every time Write-Debug or Write-Verbose are called, respectively:
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"):
You probably want to set them to Continue instead. Another source might be the -Debug switch.
Regarding your second question, it's a little long to explain, but for arguments to commands you have to put such expressions in parentheses:
Write-Debug ($RegistryEntries[$pc].Value[$app])
$currentApp = $RegistryEntries[$pc].Value[$app]

Related

Why are the indices of the arraylist added to the arraylist itself?

I'm writing a script that dynamically generates to the screen a list of AD sites that happen to contain Exchange servers (specifically, using Get-ExchangeServer | % {Get-ADSite $_.Site} | Sort -Unique) and that part works well enough when I generate that list like so:
function SiteChoiceMenu {
$sites = Get-ExchangeServer | % {Get-ADSite $_.Site} | Sort -Unique
$siteCount = $sites.count
$bullets = New-Object -TypeName System.Collections.ArrayList($null)
for ($i=0;$i -lt $siteCount;$i++) {
$index = $i+1
$bullet = "$index. " + $sites[$i].Name
$bullets.Add($bullet)
}
foreach ($bullet in $bullets) {Write-Host $bullet}
$siteChoice = Read-Host "Enter choice"
if ($siteChoice -in 1..$siteCount) { $siteChosen = $sites[$siteChoice-1]; return $siteChosen }
}
$siteChosen
When I run this code, I have 5 sites that have Exchange servers, so it generates a list like this:
1. Site 1
2. Site 2
and so forth. When I write $siteChosen to the screen, the output looks like this:
0
1
2
3
4
5
Name HubSiteEnabled
Site1 False
When I step into the code, I can see that "siteChosen" is set to the exact output above. When I look at my Arraylist, $bullets, it shows only the strings I expect it to show. Now, what I did try is using a normal array instead of an ArrayList collection object, it does not exhibit the behavior above. When I write out $siteChosen to the screen, it has just the site in it. Any ideas why?
$bullets.Add($bullet) returns the the index at which the object was added in the ArrayList. Because you don't store it in a variable, the returned int is accumulated in the pipeline.
$thorwaway = $bullets.Add($bullet) will hide it.
This is just one of those things you have to watch out for when using .NET objects and methods.

Finding out what is in an array of Hash (on a server)

I am using a 3rd party shopping cart solution that runs on a server (SellerDeck). I have some code that runs on the server to format a shopping basket with basic product data (quantity, price, name). I need to find some more data which I think is held in 2 arrays of hashes. I dont know what is contained in these 2 arrays so I would like to convert the array to a string and output via the existing code that puts it in a cookie on the client which I can then view. The 2 arrays are $pCartItem and $pProduct (see code at the bottom for how they are used).
The string $cartStr (bottom of the code) is output onto the client in a cookie by another part of the code.
I would like to covert the 2 arrays into 2 strings which can be concatenated onto $cartStr. I can then read the contents on my local pc (client). My issue is I am very unfamiliar with perl and know how to do the conversion.
I tried adding :
my $MiniCrtS=" ";
my $MiniCartElmt;
foreach $MiniCartElmt (#{$pProduct}) {
$MiniCrtS=$MiniCrtS . $MiniCartElmt;
}
and then changed the $cartStr from:
HTML::Entities::encode(substr($pProduct->{'NAME'},0,$abrv))
to:
HTML::Entities::encode(substr($MiniCrtS,0,$abrv))
but this change makes the code crash when run.
Any ideas on what I am doing wrong or an alternative to find out the data in the arrays?
Many thanks Tony
The relevant code is:
sub miniCart
{
use HTML::Entities ();
my $Self = shift;
my $abrv=12; # number of characters to abbreviate item name
my $defaultCur="£"; # currency symbol to include
my $cartStr="ss=" . $::g_sSearchScript . "cur=" . $defaultCur;
my $pCartItem;
foreach $pCartItem (#{$Self->{_CartList}})
{
my ($Status, $Message, $pProduct) = GetProduct($pCartItem->{'PRODUCT_REFERENCE'}, $pCartItem->{'SID'});
if ($Status == $::FAILURE)
{
return ($Status, $Message, []);
}
elsif ($Status == $::NOTFOUND)
{
next;
}
my #Prices = $Self->GetCartItemPrice($pCartItem);
$cartStr=$cartStr . "&!" . $pCartItem->{'QUANTITY'} . "x" . HTML::Entities::encode($pCartItem->{'PRODUCT_REFERENCE'}) . ">" . HTML::Entities::encode(substr($pProduct->{'NAME'},0,$abrv)) . ">" . $Prices[2]/100;
}
return $cartStr;
}
To get a dump of a data structure, you can use Data::Dumper.
I'd use it as follows:
use Data::Dumper qw( );
sub dumper {
local $Data::Dumper::Indent = 0;
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Useqq = 1;
return Data::Dumper::Dumper($_[0]);
}
warn("pProduct: " . dumper($pProduct));
This will log it the string produced to the web server's error log, but you could also return it in a cookie if that's what you really want.

Foreach logic issue with CakePHP

I have I problem that I hope someone can help me with. I thought this code was right, but it will not work. Below is my code, it is a function for my CakePHP 2.2.2 site, the main aim of the code is to produce a menu system from database results. The problem is with my foreach loop, it will not loop. All $Key does is return the value of 2 (three records within the table at this time). So When I display / echo / debug $Menu, the only result I get is the last result stored within the database.
I know the SQL command is right, if that is debuged / echoed then all three results are displayed. The idea of this loop was to get it to count the results, so that I could run a check on a selected field. Where I am going wrong?
function MenuSystem() {
$this->loadModel('Menu');
$MenuSQL = $this->Menu->find('all', array('conditions' => array('active' => true)));
foreach ($MenuSQL as $Key=>$Value) {
$MenuSystem = $MenuSQL[$Key];
$this->Menu = $MenuSystem;
}
}
Many Thanks,
Glenn.
UPDATE :::
Below is my function, now my foreach loop now works, don't know what I was doing wrong, but I know think its working. You can see the print_r command that I am using for testing, if I use that, then all links from my database are printed / echoed on the screen and all works. But if I try and call the $this->Menu from another controller, then only the last record is echoed on the screen. I have moved the $this->Menu outside of the foreach loop, but it made no difference, with it inside the loop or outside, it still only echoes the last record and not all three. So what I am doing wrong?
function MenuSystem() {
$this->loadModel('Menu');
$SiteBase = '/projects/cake/';
$MenuSQL = $this->Menu->find('all', array('conditions' => array('active' => true)));
foreach ($MenuSQL as $key => $Value) {
$MenuAccessLevel = $MenuSQL[$key]['Menu']['roles_id'];
if ($MenuAccessLevel == 1) {
$Path = $MenuSQL[$key]['Menu']['path'];
$Title = $MenuSQL[$key]['Menu']['title'];
$MenuSys = "<a href=\" " . $SiteBase . $Path . " \">" . $Title ."";
} else {
print ("Admin");
}
//print_r($MenuSys);
} //End of Foreach Loop
$this->Menu = $MenuSys;
} //End of function MenuSystem
So When I display / echo / debug $Menu, the only result I get is the last result stored within the database.
You're setting the value of $this->Menu within the foreach, so when the foreach is complete it will take the last value iterated over.
If you want to find the number of records matching a condition, try:
$menuCount = $this->Menu->find('count', array(
'conditions'=>array('active'=>true)
));
$this->set(compact('menuCount'));
Edit: also, by setting the value of $this->Menu within the foreach, you're overwriting the Menu model variable. This is not a good idea.
Edit2: to get the counts of rows as grouped by some value, try:
$this->Menu->virtualFields = array('count' => 'count(*)');
$counts = $this->Menu->find('all', array(
'group'=>'Role',
'fields'=>array('Role', 'count'),
));
This generates SQL to have the results grouped by the Role column. Returned fields are the name of the role, and the number of rows having that value.
If you wanted to do it with a foreach loop instead, it might look like:
$menus = $this->Menu->find('all', array('fields'=>array('id', 'Role')));
$counts = array('user'=>0, 'admin'=>0);
foreach ($menus as $menu) {
$role = $menu['Menu']['Role'];
$counts[$role] += 1;
}

Perl - Search array of words to check for a partial match in any part of an email address

Again, a whole day and I am stuck again.
I need to use an array of words or var that contains forbidden words that cannot appear in an email address.
Either:
$baddies = 'smtp mailer sysop';
or
#baddies = qw(smtp mailer sysop);
or
#baddies = qw/smtp mailer sysop/;
There are more bad words in the array too, about two dozen.
I am not running the latest version of perl so ~~ and so on are not supported.
I have a loop going on that sends the bands schedule out.
In that loop I need to check to see if the email contains any of those words.
I realize there may be some good emails that contain a match but, that is fine.
I have tried literally dozens of examples after I gave up trying to figure it out.
Latest was:
####FYI## $uaddress is from the foreach $uaddress(#addresses){ loop.
my %params = map { $uaddress => 1 } #baddies;
if(exists($params{$uaddress})) {
print "yep, it's there"; #for testing
push(#failed,"$uaddress is restricted<br />");
But, everything I tried just does not do what I need.
I even tried =~ and so on.
I AM REALLY feeling stupid about now..
I need another lesson here folks.. Thanks in advance.
Update: I also tried:
$baddies = 'smtp mailer sysop';
my #baddies = split / /, $baddies;
# iterate through the array
foreach (#baddies) {
if($_ =~ $uaddress) #I also reversed that {
print qq~$uaddress contains $_~;
}
}
Through trial, error and dumb luck, I haphazardly got this below to work:
foreach $uaddress(#addressList) {
$uaddress =~ s/\s//g;
#baddies = qw(smtp mailer sysop);
my #failed;
foreach (#baddies) {
if($uaddress =~ m/$_/i) {
push(#failed,"$uaddress is restricted because it found \"$_\" in the address<br />");
}
}
##stuff happens with emails that passed like Email::Verify
}

How do I consolidate a hash in Perl?

I have an array of hash references. The hashes contain 2 keys, USER and PAGES. The goal here is to go through the array of hash references and keep a running total of the pages that the user printed on a printer (this comes from the event logs). I pulled the data from an Excel spreadsheet and used regexes to pull the username and pages. There are 182 rows in the spreadsheet and each row contains a username and the number of pages they printed on that job. Currently the script can print each print job (all 182) with the username and the pages they printed but I want to consolidate this down so it will show: username 266 (i.e. just show the username once, and the total number of pages they printed for the whole spreadsheet.
Here is my attempt at going through the array of hash references, seeing if the user already exists and if so, += the number of pages for that user into a new array of hash references (a smaller one). If not, then add the user to the new hash ref array:
my $criteria = "USER";
my #sorted_users = sort { $a->{$criteria} cmp $b->{$criteria} } #user_array_of_hash_refs;
my #hash_ref_arr;
my $hash_ref = \#hash_ref_arr;
foreach my $index (#sorted_users)
{
my %hash = (USER=>"",PAGES=>"");
if(exists $index{$index->{USER}})
{
$hash{PAGES}+=$index->{PAGES};
}
else
{
$hash{USER}=$index->{USER};
$hash{PAGES}=$index->{PAGES};
}
push(#hash_ref_arr,{%hash});
}
But it gives me an error:
Global symbol "%index" requires explicit package name at ...
Maybe my logic isn't the best on this. Should I use arrays instead? It seems as though a hash is the best thing here, given the nature of my data. I just don't know how to go about slimming the array of hash refs down to just get a username and the total pages they printed (I know I seem redundant but I'm just trying to be clear). Thank you.
my %totals;
$totals{$_->{USER}} += $_->{PAGES} for #user_array_of_hash_refs;
And then, to get the data out:
print "$_ : $totals{$_}\n" for keys %totals;
You could sort by usage too:
print "$_ : $totals{$_}\n" for sort { $totals{$a} <=> $totals{$b} } keys %totals;
As mkb mentioned, the error is in the following line:
if(exists $index{$index->{USER}})
However, after reading your code, your logic is faulty. Simply correcting the syntax error will not provide your desired results.
I would recommend skipping the use of temporary hash within the loop. Just work with the a results hash directly.
For example:
#!/usr/bin/perl
use strict;
use warnings;
my #test_data = (
{ USER => "tom", PAGES => "5" },
{ USER => "mary", PAGES => "2" },
{ USER => "jane", PAGES => "3" },
{ USER => "tom", PAGES => "3" }
);
my $criteria = "USER";
my #sorted_users = sort { $a->{$criteria} cmp $b->{$criteria} } #test_data;
my %totals;
for my $index (#sorted_users) {
if (not exists $totals{$index->{USER}}) {
# initialize total for this user
$totals{$index->{USER}} = 0;
}
# add to user's running total
$totals{$index->{USER}} += $index->{PAGES}
}
print "$_: $totals{$_}\n" for keys %totals;
This produces the following output:
$ ./test.pl
jane: 3
tom: 8
mary: 2
The error comes from this line:
if(exists $index{$index->{USER}})
The $ sigil in Perl 5 with {} after the name means that you are getting a scalar value out of a hash. There is no hash declared by the name %index. I think that you probably just need to add a -> operator so the problem line becomes:
if(exists $index->{$index->{USER}})
but not having the data makes me unsure.
Also, good on you for using use strict or you would be instantiating the %index hash silently and wondering why your results didn't make any sense.
my %total;
for my $name_pages_pair (#sorted_users) {
$total{$name_pages_pair->{USER}} += $name_pages_pair->{PAGES};
}
for my $username (sort keys %total) {
printf "%20s %6u\n", $username, $total{$username};
}

Resources