Generate 2 different list in one foreach loop with powershell - powershell

I stucked in foreach part.I couldn't find any solution for generating 2 different lists in one foreach loop.I used 2 foreach but it didn't help.Below side I shared my desire output.
My code:
$InStuff = #'
a
b
c
'#.Split("`n").Trim()
$InStuff2 = #'
1
2
3
'#.Split("`n").Trim()
$SPart_1 = 'application="'
$SPart_2 = ' path='
$SPart_3 = ' name='
$SPart_4 = ' application'
foreach ($IS_Item in $InStuff) {
foreach ($IS2_Item in $InStuff2) {
$UName = $IS_Item
$UName2 = $IS2_Item
$Sentence = -join (
$SPart_1, $UName,
$SPart_2, $UName2,
$SPart_3, $UName2,
$SPart_4
)
''
$Sentence
}
}
Fail output :
application="a path=1 name=1 application
application="a path=2 name=2 application
application="a path=3 name=3 application
application="b path=1 name=1 application
application="b path=2 name=2 application
application="b path=3 name=3 application
application="c path=1 name=1 application
application="c path=2 name=2 application
application="c path=3 name=3 application
My desire output :
application="a path=1 name=1 application
application="b path=2 name=2 application
application="c path=3 name=3 application
Thank you

use a for loop:
$InStuff = #'
a
b
c
'#.Split("`n").Trim()
$InStuff2 = #'
1
2
3
'#.Split("`n").Trim()
$SPart_1 = 'application="'
$SPart_2 = ' path='
$SPart_3 = ' name='
$SPart_4 = ' application'
for ($i = 0; $i -lt $InStuff.count; $i++) {
$Sentence = -join (
$SPart_1, $InStuff[$i],
$SPart_2, $InStuff2[$i],
$SPart_3, $InStuff2[$i],
$SPart_4
), ''
$Sentence
}
This will likely go wrong if your input arrays are not the same length, so it is not that safe. Perhaps using a hash or custom object would be a better idea:
$arr = #()
$arr += new-object PSCustomObject -property #{application='a';path=1;name=1}
$arr += new-object PSCustomObject -property #{application='b';path=2;name=2}
$arr += new-object PSCustomObject -property #{application='c';path=3;name=3}
$arr | % { 'application="{0} path={1} name={2}' -f $_.application, $_.path, $_.name }

#arco444 is right, no matter what you will have problems if your lists are different lengths. You should reconsider how you are collecting and formatting the data. Here is an alternative method:
$InStuff = "a","b","c"
$InStuff2 = 1,2,3
$listCount = $InStuff.Count
$x = 0
do {
$strOut = "application= `"path = {0} name = {1} application`"" -f $InStuff[$x], $InStuff2[$x]
$strOut
$x++
}
while ( $x -lt $listCount )
Not sure what you want with a stray " in there, I've added one to enclose the output:
application= "path = a name = 1 application"
application= "path = b name = 2 application"
application= "path = c name = 3 application"
If you plan to use this output for further processing by PowerShell, like putting it in a csv with Export-Csv then you should forgo the application text and create an object instead:
$InStuff = "a","b","c"
$InStuff2 = 1,2,3
$listCount = $InStuff.Count
$x = 0
do {
[pscustomobject]#{
path = $InStuff[$x]
name = $InStuff2[$x]
}
$x++
}
while ( $x -lt $listCount )
While that's not exactly what you are asking for, it's been my experience that data in this format is far more useful:
path name
---- ----
a 1
b 2
c 3
you can add lines to
[pscustomobject]#{
path = $InStuff[$x]
name = $InStuff2[$x]
}
for the additional text (if it's a must) and do something like this:
[pscustomobject]#{
type = "application"
path = $InStuff[$x]
name = $InStuff2[$x]
}
and that will add a column for the word application

Related

Hash Table, Multiples values in Key, Foreach Loop Powershell

I have filled the keys with the necessary values,
Every key will have multiples values
$vms = #{}
$vms.template += $templateName
$vms.name += $vmName
$vms.EsxiHostName += $esxiHostName
$vms.datastore += $datastoreName
$vms.network += $networkName
$vms.FolderLocation += $folderName
$vms.vCPU += $vCPU
$vms.CoresPerCPU += $vmCores
$vms.Memory += $vmRam
$vms.IP += $vmIP
$vms.SubnetMask += $vmMask
$vms.gateway += $vmGateway
$vms.DNS1 += $vmDns1
$vms.DNS2 += $vmDns2
$vms.Description += $vmDescription
$vms.TrendMicroScanDay += $tmscanday
$vms.inventory_billing_owner += $inventoryBillingOwner
And now what I want to do is something like this because I want to use these variables in another commands.
foreach ($vm in $vms) {
#Assign Variables
$VCTemplate = $vm.template
$VMName = $vm.Name
$VMHost = $vm.EsxiHostName
$Datastore = $vm.datastore
$NetworkName = $vm.network
$FolderLocation = $vm.FolderLocation
$vCPU = $vm.vCPU
$CoresPerCPU = $vm.CoresPerCPU
$Memory = $vm.Memory
$VMIP = $vm.IP
$SubnetMask = $vm.SubnetMask
$GW = $vm.Gateway
$DNS1 = $vm.DNS1
$DNS2 = $vm.DNS2
$Description = $VM.Description
$TrendMicroScanDay = $VM.TrendMicroScanDay
$inventory_billing_owner = $VM.inventory_billing_owner
}
It seems foreach loop doesn't work this way and I try to find information about it but was not possible
Someone know how can I work with a Foreach Loop and a Hash Table with multiples values per key?
Thanks
EDIT:
Thanks Mclayton for answer, I tried your solutions
First I want to send you what is inside of $vms
PS C:\Users\me\Desktop> $vms
Name Value
---- ----- SubnetMask {255.255.255.0, 255.255.255.255} description {TEST, Test 2}
Memory {4, 8}
name {Name1, Test 2}
vCPU {4, 8}
ip {10.10.10.1, 20.20.20.1} datastore {vsanDatastore, vsanDatastore} dns2 {10.10.10.5, 20.20.20.5}
gateway {10.10.10.3, 20.20.20.3}
template {ESSQLTEMPLATE01, WIN 10 Template}
FolderLocation {Office Domain, SysAdmin LAB}
TrendMicroScanDay {Day5, Day5}
CoresPerCPU {4, 8}
dns1 {10.10.10.4, 20.20.20.4}
EsxiHostName {es1esxi01p, es1esxi02p}
network {servers, data2}
Then with the first option running this for test
for($i = 0; $i -lt $vms.template.Length; $i++ )
{
$VCTemplate = $vms.template[$i];
$VMName2 = $vms.Name[$i];
}
PS C:\Users\me\Desktop> $VCTemplate
WIN 10 Template
I'm getting the second value, maybe I didn’t understand what you were saying
And with the second option, I was thinking what to use in the foreach ($something in $something_else)
but I ran this:
$vm3 = #()
$vm3 += new-object PSCustomObject -Property ([ordered] #{
Template = $vms.template
Name = $vms.name
EsxiHostName = $vms.EsxiHostName
datastore = $vms.datastore
network = $vms.network
FolderLocation = $vms.FolderLocation
vCPU = $vms.vCPU
CoresPerCPU = $vms.CoresPerCPU
Memory = $vms.Memory
IP = $vms.IP
SubnetMask = $vms.SubnetMask
gateway = $vms.gateway
DNS1 = $vms.DNS1
DNS2 = $vms.DNS2
Description = $vms.Description
TrendMicroScanDay = $vms.TrendMicroScanDay
})
foreach ($vm in $vm3)
{
write-host 'This is '$vm.template
}
And this was the result
PS C:\Users\me\Desktop> foreach ($vm in $vm3)
{
write-host 'This is '$vm.template
}
This is ESSQLTEMPLATE01 WIN 10 Template
In your code, $vms is a single hashtable object, and if you foreach() over a single object the loop will only run once. The fact that all of $vms's properties (e.g. $vms.template) are arrays doesn't make any difference to this.
If you really need to use a single hastable with properties that are parallel arrays, what you'll need to do is something like:
for($i = 0; $i -lt $vms.template.Length; $i++ )
{
$VCTemplate = $vms.template[$i];
$VMName = $vms.Name[$i];
... etc ...
... now do stuff with the $i'th vm ...
write-host $vmName;
}
but a better alternative would be to create $vms as an array of objects with #() (note round brackets not squiggly ones) - e.g.
$vms = #()
foreach( $something in $something_else )
{
$vms += new-object PSCustomObject -Property ([ordered] #{
Template = $something.template
Name = $something.name
... etc ...
})
}
and then you can iterate over $vms:
foreach ($vm in $vms)
{
write-host $vm.Name
}

Powershell Wrong output

guys i need help here, i want to return the $Location002 and $Location003 content look what is in output
$Location = "westus2"
$Location002 = "westeurope"
$Location003 = "eastasia"
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
$i = $_
# define name for VM, will be used for other resources
if ($i -eq 1) {
$locname = "$Location"
Write-Output $locname
}
else {
$locname = $("Location00" + "$i")
Write-Output $locname
}
}
output :
PS C:\Users\Marouane\Desktop\testpowershell> c:\Users\Marouane\Desktop\testpowershell\test.ps1
How many VMs?: 3
westus2
Location002
Location003
PS C:\Users\Marouane\Desktop\testpowershell>
i need to output westeurope and eastasia
Using a separate variable for each value in a group or list of things is a bit of an anti-pattern, you'll want to put them all in an array together instead:
# Define array of possible locations
# `$Locations[0]` will resolve to `westus2`
# `$Locations[1]` will resolve to `westeurope`, etc.
$Locations = #(
"westus2"
"westeurope"
"eastasia"
)
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
# Define the VM name
$name = "VirtualMachine$_"
# Pick next location from the $Locations array
# the % ensures we "wrap around" when we reach the end
$location = $Locations[($_ - 1) % $Locations.Length]
# Output a new object with Name + Chosen Location
[pscustomobject]#{
VMName = $name
Location = $location
}
}
Output for 3 VMs:
How many VMs?: 3
VMName Location
------ --------
VirtualMachine1 westus2
VirtualMachine2 westeurope
VirtualMachine3 eastasia
This is not how I would do it, but the more immediate problem is you're assigning a concatenated string to $location and writing to the output stream. I think what you want to do is reference the value of the earlier variable.
There are some clever syntaxes for that. I struggle to remember them. However below would be a start.
$Location = "westus2"
$Location002 = "westeurope"
$Location003 = "eastasia"
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
$i = $_
# define name for VM, will be used for other resources
if ($i -eq 1) {
$name = "$VMName"
$locname = "$Location"
Write-Output $locname
}
else {
$name = "$VMName" + "00" + "$i"
$locname = (Get-Variable ("Location00" + "$i")).Value
Write-Output $locname
}
}
Update With Alternative:
I'm still not sure what the goal is, but based on the original sample it would seem there's a 1 to 1 relationship between the location# and the VM number. That said if you go past the number of VMs you would have to adjust this to pick according to the intended pattern...
$Locations = 'westus2', 'westeurope', 'eastasia'
[int]$VMCount = Read-Host 'How many VMs?'
For( $i = 0; $i -lt $VMCount ; ++$i )
{
$Locations[$i]
}
Further Update:
Respective to Mathias's good answer :
$Locations = 'westus2', 'westeurope', 'eastasia'
[int]$VMCount = Read-Host 'How many VMs?'
For( $i = 0; $i -lt $VMCount ; ++$i )
{
$Locations[ $i % $Locations.Count ]
}
Using the modulus operator in this pattern is very efficient for distributing one list over another. I wrote a small post about My Modulus Obsession with this and some other uses.
You'll need to retrieve the variable's content with Get-Variable. You can also avoid the extra step of making an $i variable and instead use the automatic variable $_
$Location = "westus2"
$Location002 = "westeurope"
$Location003 = "eastasia"
[int]$VMCount = Read-Host "How many VMs?"
1..$VMCount | ForEach-Object {
# define name for VM, will be used for other resources
if ($_ -eq 1) {
$name = "$VMName"
$locname = "$Location"
Write-Output $locname
}
else {
$name = "$VMName" + "00" + "$_"
$locname = "Location00" + "$_"
Write-Output (get-variable $locname).value
}
}

how to use a FOR loop variable to help define another variable

I am new to powershell scripts and not sure how to achieve the below:
$finalArray = #()
$tempArray0 = 'A'
$tempArray1 = 'B'
$tempArray2 = 'C'
FOR (i=0; i -eq 5; i++) {$finalArray += $tempArray[i]}
$finalArray
Output Should be:
A
B
C
If the variable name is itself variable, you'll have to use the Get-Variable cmdlet to retrieve its value:
$finalArray = #()
$tempArray0 = 'A'
$tempArray1 = 'B'
$tempArray2 = 'C'
for($i=0; $i -le 2; $i++) {
$finalArray += (Get-Variable "temparray$i" -ValueOnly)
}
$finalArray
If you want to create variables with variable names, use the New-Variable cmdlet:
$Values = 'A','B','C'
for($i = 0; $i -lt $Values.Count; $i++){
New-Variable -Name "tempvalue$i" -Value $Values[$i]
}
which would result in:
PS C:\> $tempvalue1
B
Although the above will solve the example you've presented, I can think of very few cases where you wouldn't be better of using a [hashtable] instead of variable variable names - they're usually an over-complication, and you'll end up with unnecessary code anyways because you need to calculate the variable names at least twice (during creation and again when reading the value).
From the comments, it sounds like you're trying to generate input for a password generator. This can be simplified grossly, without resorting to variable variable names:
# Create a hashtable and generate the characters
$CharArrays = #{
Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
Numbers = 0..9
}
# Generate some letters for the password
$PasswordChars = $CharArrays['Letters'] |Get-Random -Count 10
# Generate a few digits
$PasswordChars += $CharArrays['Numbers'] |Get-Random -Count 4
# Shuffle them around a bit
$PasswordChars = $PasswordChars |Sort-Object {Get-Random}
# Create your password
$Password = $PasswordChars -join ''

How to create ForEach loop to go through multiple variables Powershell

I've got a problem with ForEach loop. Im trying to loop through multiple variables of same kind just increment different.
Im trying to change the TextBox Text depending on if Label from same row has text.
This is how I could make it to just write and IF sentence for each Label but I was looking a way to loop each of these blocks through ForEach loop. I've got total of 8 Labels and Textboxes.
Here is the code: ( Im sure you'll figure out what I'm after :) )
IF ( $Label1.Text.Length -ne 0 )
{
$Label1.Visible = $true
$TextBox1.Visible = $true
$TextBox1.Text = ( "Enter new name for " + $Label1.Text )
}
example of ForEach
$Count = 1..8
$Count | ForEach-Object {
IF ( $Label($_).Text.Length -ne 0 )
{
$Label($_).Visible = $true
$TextBox($_).Visible = $true
$TextBox($_).Text = ( "Enter new name for " + $Label($_).Text )
}
}
etc...
I tried putting variables in array and loop through that way but ofcourse array changes the type to string and it doesnt work...
Give this a try, I can't test it using label & textbox object but it can work tuning it better:
1..8 | ForEach-Object {
IF ( (iex "`$Label$_.Text.Length") -ne 0 )
{
iex "`$Label$_.Visible = `$true"
iex "`$TextBox$_.Visible = `$true"
iex "`$TextBox$_.Text = 'Enter new name for ' + `$Label$_.Text"
}
}
You can use the Get-Variable cmdlet for that purpose:
1..8 | ForEach-Object {
if ( (Get-Variable "Label$_").Value.Text.Length -ne 0 )
{
(Get-Variable "Label$_").Value.Visible = $true
(Get-Variable "Label$_").Value.Visible = $true
(Get-Variable "Label$_").Value.Text = ( "Enter new name for " + (Get-Variable "Label$_").Value.Text )
}
}

Updating hash table values in a 'foreach' loop in PowerShell

I'm trying to loop through a hash table and set the value of each key to 5 and PowerShell gives an error:
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
foreach($key in $myHash.keys){
$myHash[$key] = 5
}
An error occurred while enumerating through a collection:
Collection was modified; enumeration operation may not execute..
At line:1 char:8
+ foreach <<<< ($key in $myHash.keys){
+ CategoryInfo : InvalidOperation: (System.Collecti...tableEnumer
ator:HashtableEnumerator) [], RuntimeException
+ FullyQualifiedErrorId : BadEnumeration
What gives and how do I resolve this problem?
You can't modify Hashtable while enumerating it. This is what you can do:
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$myHash = $myHash.keys | foreach{$r=#{}}{$r[$_] = 5}{$r}
Edit 1
Is this any simpler for you:
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
foreach($key in $($myHash.keys)){
$myHash[$key] = 5
}
There is a much simpler way of achieving this. You cannot change the value of a hashtable while enumerating it because of the fact that it's a reference type variable. It's exactly the same story in .NET.
Use the following syntax to get around it. We are converting the keys collection into a basic array using the #() notation. We make a copy of the keys collection, and reference that array instead which means we can now edit the hashtable.
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
foreach($key in #($myHash.keys)){
$myHash[$key] = 5
}
You do not need to clone the whole hashtable for this example. Just enumerating the key collection by forcing it to an array #(...) is enough:
foreach($key in #($myHash.keys)) {...
Use clone:
foreach($key in ($myHash.clone()).keys){
$myHash[$key] = 5
}
Or in the one-liner:
$myHash = ($myHash.clone()).keys | % {} {$myHash[$_] = 5} {$myHash}
I'm new to PowerShell, but I'm quite a fan of using in-built functions, because I find it more readable. This is how I would tackle the problem, using GetEnumerator and Clone. This approach also allows one to reference to the existing hash values ($_.value) for modifying purposes.
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$myHash.Clone().GetEnumerator() | foreach-object {$myHash.Set_Item($_.key, 5)}
You have to get creative!
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$keys = #()
[array] $keys = $myHash.keys
foreach($key in $keys)
{
$myHash.Set_Item($key, 5)
}
$myHash
Name Value
---- -----
c 5
a 5
b 5
As mentioned in a previous answer, clone is the way to go. I had a need to replace any null values in a hash with "Unknown" nd this one-liner does the job.
($record.Clone()).keys | %{if ($record.$_ -eq $null) {$record.$_ = "Unknown"}}
$myHash = #{
Americas = 0;
Asia = 0;
Europe = 0;
}
$countries = #("Americas", "Asia", "Europe", "Americas", "Asia")
foreach($key in $($myHash.Keys))
{
foreach($Country in $countries)
{
if($key -eq $Country)
{
$myHash[$key] += 1
}
}
}
$myHash
$myHash = #{
Americas = 0;
Asia = 0;
Europe = 0;
}
$countries = #("Americas", "Asia", "Europe", "Americas", "Asia")
foreach($key in $($myHash.Keys))
{
foreach($Country in $countries)
{
if($key -eq $Country)
{
$myHash[$key] += 1
}
}
}
Updating a hash value if array elements matched with a hash key.
It seems when you update the hash table inside the foreach loop, the enumerator invalidates itself. I got around this by populating a new hash table:
$myHash = #{}
$myHash["a"] = 1
$myHash["b"] = 2
$myHash["c"] = 3
$newHash = #{}
foreach($key in $myHash.keys){
$newHash[$key] = 5
}
$myHash = $newHash