PowerCLI Snapshots Array - arrays

Slightly stumped right now and was wondering if the community could give me that quick boost to help me continue with a program I'm working.
At one point in the program that I'm working on, I'm trying to get the 6 newest elements from an array. I want to put the snapshot variable inside of an array in order to get all the snapshots inside of an array. Here's the part of the code that is confusing me at the moment:
$server = "test"
$date = get-date
$tempArray = #()
$snapshot = get-snapshot -VM "test"
foreach ($item in $snapshot){
$tempArray += $item
}
$tempArray | sort
for ($i = 0; $i -le $tempArray.length-6; $i++){
remove-item $tempArray[$i]
}
Am I achieving my goal of getting the $snapshot variable inside of my array and is my for loop correctly managing to delete all but the 6 newest?
EDIT: Fixed small issues that hadn't noticed before.

you code has several issues. i am not sure if this will fix your script but these seem to be obvious problems that you should fix first.
foreach ($item in $snapshot){
$tempArray++ -> this should be $tempArray += $item, right? if you are adding $item to the tempArray
}
$tempArray | sort
for ($i = 0; $i -le $tempArray.length-6; $i++){
remove-item $snapshot -> this should be remove-item $tempArray[$i], right?
}

Reverse sort by the created timestamp attribute then use the Skip in select object to get everything after the 6 newest
$snapshot = get-snapshot -VM "test"
$snapshot | sort created -descending | select -Skip 6 | Remove-Snapshot -Confirm:$false

Related

Cannot remove a string from an array in powershell

I'm trying to populate an array of file paths where ever the script is located in. But I don't want
the array to include the path of the script only the other files in that folder. I have tried removing it after it is populated by using a list array instead but then I get an error that the array is a fixed size.
#To get path in which the script is located
$mypath = $MyInvocation.MyCommand.Path
$myStringPath=$mypath.ToString().Replace("TestingScriptPath.ps1", "")
#Populates files inside the folder
$array = #()
(Get-ChildItem -Path $myStringPath ).FullName |
foreach{
$array += $_
}
#display paths
for($i = 0; $i -lt $array.length; $i++)
{
$array[$i]
}
You're better off not putting it in the array in the first place.
When updating an array the whole array has to be rewritten, so the performance tends to be terrible.
Use a different datatype if you're going to be removing item-by-item.
#To get path in which the script is located
$mypath = $MyInvocation.MyCommand.Path
$myStringPath=$mypath.ToString().Replace("testingscriptpath.ps1", "")
#Populates files inside the folder
$array = Get-ChildItem -Path $myStringPath | Where-Object {$_.fullname -ne $mypath}
$array
if you definitely want to do it the way suggested in the question (slower)
$ArrayWithFile = Get-ChildItem -Path $myStringPath
$ArrayWithoutFile = $ArrayWithFile | Where-Object {$_.fullName -ne $mypath}

Correct usage of ForEach -parallel in Powershell?

I want to process a large number of URLs and grab the *.jpg file locations.
The problem is that the $entry in the second foreach is not threadsafe.
The script is firing hundreds of errors because the $entry is getting overwritten over and over.
When I move the inner foreach outside of the ForEach-Object, then its working fine but very slowly.
How can I process the split output properly within my ForEach-Object without getting these errors?
$array just contains a huge amount of URLs
$clean_img_array is the output array of the operation
$tmpArray is the reference to $clean_img_array in order to use it within a parallel ForEach
Errors:
InvalidOperation:
Line |
14 | [void]$tmpArray:clean_img_array.Add($entry);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| You cannot call a method on a null-valued expression.
Snippet:
$clean_img_array = [System.Collections.ArrayList]#();
$array | ForEach-Object -Parallel {
$web = Invoke-RestMethod $_;
$i=1;
foreach($entry in $web.Split("`"")){
echo $entry;
if($entry.IndexOf(".jpg") -ne -1 -And $entry.IndexOf("http") -ne -1){
if($entry.IndexOf("?") -ne -1){
$tmpArray = $using:clean_img_array;
[void]$tmpArray.Add($entry.Substring(0, $entry.IndexOf('?')));
}else{
$tmpArray = $using:Clean_img_array;
[void]$tmpArray:clean_img_array.Add($entry);
}
}
}
} -ThrottleLimit 20
Here's a simple example. Both $a and $b are arrays. $b is the result of the parallel loop. It's like example 12 in the docs.
$a = 1..10
$b = $a | foreach-object -parallel { $_ + 1 }
$b
2
3
4
5
6
7
8
9
10
11
Thanks for the support!
I combined the answer from #js2012 with something of my own.
The return alone did not solve the thread unsave behavior of $entry, but clearly untangled the workflow.
But I used the inline version of .foreach with the pipe line variable $_ wich happens
to be thread safe as it appears to me. Running now like a charm and also very fast for more than 2million entrys.
$array holds the URLs to be processed
$clean_img_array returns the grabbed image URLs
$clean_img_array = $array | ForEach-Object -Parallel {
$web = Invoke-RestMethod $_;
$web.Split("`"").foreach({
if($_ -ne $null){
if($_.IndexOf(".jpg") -ne -1 -And $_.IndexOf("http") -ne -1){
if($_.IndexOf("?") -ne -1){
return $_.Substring(0, $entry.IndexOf('?'));
}else{
return $_;
}
}
}
});
} -ThrottleLimit 25

Powershell - remove duplicates from 2 arrays using wildcards

I am trying to remove duplicates and leave only unique entries from the output of 2 queries.
I am pulling a list of installed Windows Updates using the following (also stripping 12 chars of whitespace and dropping to lower case:
$A = #(Get-HotFix | select-object #{Expression={$_.HotFixID.ToLower()}} | ft -hidetableheaders | Out-String) -replace '\s{12}',''
I am then querying a list of available files in a folder and stripping 3 trailing whitespace chars using:
$B = #(Get-ChildItem D:\y | select-object 'Name' | ft -hidetableheaders | Out-String) -replace '\s{3}',''
The problem I have is that the first query ($A) returns output like:
kb4040981
kb4041693
kb2345678
kb8765432
While the second query ($B) returns output like:
windows8.1-kb4040981-x64_d1eb05bc8c55c7632779086079c7759f40d7386f.cab
windows8.1-kb4041687-x64_3bdf264bcfc0dda01c2eaf2135e322d2d6ce6c64.cab
windows8.1-kb4041693-x64_359b7ac71a48e5af003d67e3e4b80120a2f5b570.cab
windows8.1-kb4049179-x64_e6ec21d5d16fa6d8ff890c0c6042c2ba38a1f7c4.cab
I need to compare the 2 outputs using wildcards around each entry in the $A array (I think), and where it exists in $B remove the entire line from $B array.
I cannot truncate the output of $B as I need to use the full filenames in a subsequent process.
IE in the example output above, the entire FIRST and THIRD lines would be remove from the $B array and other lines left intact.
I have tried numerous methods from online searches, and used foreach loops, all to no avail.
Thank you in advance for any assistance.
What did you try with foreach loops that didn't work? Unless your output is huge, this method is pretty striaght forward.
$a = "kb4040981","kb4041693","kb2345678","kb8765432","test"
[System.Collections.ArrayList]$b = "windows8.1-kb4040981-x64_d1eb05bc8c55c7632779086079c7759f40d7386f.cab","windows8.1-kb4041687-x64_3bdf264bcfc0dda01c2eaf2135e322d2d6ce6c64.cab","windows8.1-kb4041693-x64_359b7ac71a48e5af003d67e3e4b80120a2f5b570.cab","windows8.1-kb4049179-x64_e6ec21d5d16fa6d8ff890c0c6042c2ba38a1f7c4.cab"
$toRemove = New-Object System.Collections.ArrayList
foreach($kb in $a)
{
foreach($line in $b)
{
if($line -match $kb)
{
write-host "$kb found in: $line" -ForegroundColor Green
$toRemove.add($line) | out-null
}
}
}
foreach($line in $toRemove)
{
$b.Remove($line)
}
$b
Hope it helps.
I would recommend for you to take a little time to learn the very basics of Powershell. When you use format cmdlets and text files instead of objects you cut yourself of the good stuff. ;-)
Here is how I would start the task:
$A = Get-HotFix
$B = Get-ChildItem D:\y | Select-Object -Property Name,#{Name='HotFixID';Expression={($_.BaseName -split '-')[1]}}
Compare-Object -ReferenceObject $A -DifferenceObject $B -Property 'HotFixID' -PassThru
Sincere thanks to sambardo for his patience and input! The final working solution based on his excellent recommendation is:
$a = (Get-Hotfix).hotfixID
$b = (Get-ChildItem D:\y\ -file *.cab).name
$toRemove = New-Object System.Collections.ArrayList
foreach($kb in $a)
{
foreach($line in $b)
{
if($line -match $kb)
{
# write-host "$kb found in: $line" -ForegroundColor Green
$toRemove.add($line) | out-null
}
}
}
foreach($line in $toRemove)
{
$b.Remove($line)
}
$b

Powershell compare one array as substring of another array

I have a text file domains.txt
$domains = ‘c:\domains.txt’
$list = Get-Content $domains
google.com
google.js
and an array
$array = #(".php",".zip",".html",".htm",".js",".png",".ico",".0",".jpg")
anything in $domain that ends in something in #arr should NOT be in my final list
So google.com would be in final list but google.js would not.
I found some other stackoverflow code that give me the exact opposite of what I'm looking for but, hah I can't get it reversed!!!!
This gives me the exact opposite of what I want, how do I reverse it?
$domains = ‘c:\domains.txt’
$list = Get-Content $domains
$array = #(".php",".zip",".html",".htm",".js",".png",".ico",".0",".jpg")
$found = #{}
$list | % {
$line = $_
foreach ($item in $array) {
if ($line -match $item) { $found[$line] = $true }
}
}
$found.Keys | write-host
this gives me google.js I need it to give me google.com.
I've tried -notmatch etc and can't get it to reverse.
Thanks in advance and the more explanation the better!
Take the .s off, mash the items together into a regex OR, tag on an end-of-string anchor, and filter the domains against it.
$array = #("php","zip","html","htm","js","png","ico","0","jpg")
# build a regex of
# .(php|zip|html|htm|...)$
# and filter the list with it
$list -notmatch "\.($($array -join '|'))`$"
Anyway, the simple way to invert your result is to walk through $found.keys | where { $_ -notin $list }. Or to change your test to $line -notmatch $item.
But beware that you are doing a regex match and something like top500.org would match .0 and throw your results out. If you need to match at the end specifically, you need to use something like $line.EndsWith($item).
other solution
$array = #(".php",".zip",".html",".htm",".js",".png",".ico",".0",".jpg")
get-content C:\domains.txt | where {[System.IO.Path]::GetExtension($_) -notin $array}

Print Array Elements on one line

I'm populating an array variable $array at some point in my code, for example like below
this
is
an
array
varaible
What if, I wanted to print out the array variable like thisisanarrayvariable as one liner
i took the below approach, but i'am not getting any out while the program is hanging
for ($i=0;$i -le $array.length; $i++)
{ $array[$i] }
obviuosly, i dont want to glue them together like $array[0]+$array[1]+$array[2]..
Hope i can get a better answer.
Joining array elements with no separator
Use the -join operator...
$array -join ''
...or the static String.Join method...
[String]::Join('', $array)
...or the static String.Concat method...
[String]::Concat($array)
For all of the above the result will be a new [String] instance with each element in $array concatenated together.
Fixing the for loop
Your for loop will output each element of $array individually, which will be rendered on separate lines. To fix this you can use Write-Host to write to the console, passing -NoNewline to keep the output of each iteration all on one line...
for ($i = 0; $i -lt $array.Length; $i++)
{
Write-Host -NoNewline $array[$i]
}
Write-Host
The additional invocation of Write-Host moves to a new line after the last array element is output.
If it's not console output but a new [String] instance you want you can concatenate the elements yourself in a loop...
$result = ''
for ($i = 0; $i -lt $array.Length; $i++)
{
$result += $array[$i]
}
The += operator will produce a new intermediate [String] instance for each iteration of the loop where $array[$i] is neither $null nor empty, so a [StringBuilder] is more efficient, especially if $array.Length is large...
$initialCapacity = [Int32] ($array | Measure-Object -Property 'Length' -Sum).Sum
$resultBuilder = New-Object -TypeName 'System.Text.StringBuilder' -ArgumentList $initialCapacity
for ($i = 0; $i -lt $array.Length; $i++)
{
$resultBuilder.Append($array[$i]) | Out-Null # Suppress [StringBuilder] method returning itself
}
$result = $resultBuilder.ToString()
Just use
-join $array
which will glue all elements together.

Resources