Sh3perd
Sh3perd

Reputation: 89

Powershell foreach regarding multiple collections

So i have a powershell script im toying with that i'd like to ask the community's help. I have to preface that I am not always the best in communicating what I am attempting to do, partly because i dont have programming experience so please bear with me, and ask questions/correct me if i use incorrect words to explain what i mean.

With that said, Here's what I am trying to do:

while incrementing both services and startuptypes:

So assume $services has:
bits appmgmt

and $startuptypes has:

Automatic Manual

i want them to be applied respectively (bits > automatic appmgmt > manual)

Heres what i have thus far:

$services = Get-Content "C:\TEMP\services.txt"
$Startuptypes = Get-Content "C:\TEMP\StartupTypes.txt"
$RebootingServer = Read-Host 'Name of the server that you are bringing down'
$FailoverServer = Read-Host 'Name of the server it is failing over to'


#foreach ($service in $services && $Startuptype in $Startuptypes) {

Invoke-Command -ComputerName $RebootingServer -ArgumentList $service - ScriptBlock {param($service) Stop-Service $service}
Start-Sleep -s 3
Invoke-Command -ComputerName $RebootingServer -ArgumentList $service - ScriptBlock {param($service) set-service $service -StartupType Disabled}
Start-Sleep -s 10
Invoke-Command -ComputerName $FailoverServer -ArgumentList $service $StartupType -ScriptBlock {param($service,$startuptype) Set-Service $service -StartupType $startuptype}
Start-Sleep -s 3
Invoke-Command -ComputerName $FailoverServer -ArgumentList $service - ScriptBlock {param($service) Start-Service $service}
Start-sleep -s 10
}

The 'for each' statement is pseudo-code of what i want it to do, yet unsure if that exists or how to write it accordingly. I dont even know what that would be appropriately called. Multiple conditionals? That aside, how would i properly write what I am attempting to accomplish? Thank you for any help in advanced.

Upvotes: 1

Views: 1090

Answers (1)

mklement0
mklement0

Reputation: 440317

It sounds like you want to enumerate the elements of 2 collections in corresponding pairs: in iteration 1, process element 1 from collection A along with element 1 from collection B, ...

Note:

  • This answer assumes that the input collections have the same length. See this answer for a solution if they do not.

PowerShell 6- solution:

# Sample collections.
# Note that their counts must match.
$services     = 'serviceA', 'serviceB', 'serviceC'
$startupTypes = 'automatic', 'manual', 'disabled '


$i = 0 # helper index var.
foreach ($service in $services) { # enumerate $services directly

   # Using the index variable, find the corresponding element from 
   # the 2nd collection, $startupTypes, then increment the index.
   $startupType = $startupTypes[$i++]

   # Now process $service and $startupType as needed.
}

PowerShell 7+ solution:

PowerShell 7 is built on .NET Core 3.1, where the System.Linq.Enumerable.Zip has an overload that returns (2-element) value tuples (System.ValueTuple<T1, T2>), which simplifies the solution:

  • Caveat: With input collections of unequal length, enumeration stops once the smaller collection has run out of items.
# Sample collections.
# Note: Due to use of [Linq.Enumerable]::Zip() below:
#  * The element counts need *not* match, because pairing stops 
#    once the shorter collection has run out of elements.
#  * However, note that strongly typed type constraint ([string[]]),
#    which is required for PowerShell to find the right [Linq.Enumerable]::Zip()
#    method overload. You can also *cast* on demand. 
[string[]] $services     = 'serviceA', 'serviceB', 'serviceC'
[string[]] $startupTypes = 'automatic', 'manual', 'disabled '

# Enumerate the collection in pairs (2-element value tuples).
# Note that the properties containing the tuple elements are
# named .Item1 and .Item2, but PowerShell allows you to access them 
# by positional index too.
[Linq.Enumerable]::Zip($services, $startupTypes) | ForEach-Object {
  "service: {0}; startup type: {1}" -f $_[0], $_[1]
}

Generally, note that PowerShell, as of 7.3, lacks support for .NET extension methods (which is why explicit use of [System.Linq.Enumerable] is required here) and also lacks comprehensive support for calling generic methods, though there are feature requests on GitHub - see GitHub issue #2226 and GitHub issue #5146.

The above yields:

service: serviceA; startup type: automatic
service: serviceB; startup type: manual
service: serviceC; startup type: disabled 

Upvotes: 1

Related Questions