Powershell 使用开始作业同时运行多个脚本块(而不是循环)
大家好强> 我一直在寻找一种使我的脚本更高效的方法,我已经得出结论(在StackOverflow上的好人的帮助下),开始工作是一条路要走 我希望在$servers中的所有服务器上同时运行以下foreach循环。我无法理解如何实际收集从接收作业返回的信息并将其添加到$serverlist PS:我知道我离把这件事搞清楚还有很长的路要走,但我真的很想得到一些帮助,因为我对如何开始工作和接受工作感到困惑 注释Powershell 使用开始作业同时运行多个脚本块(而不是循环),powershell,foreach,jobs,Powershell,Foreach,Jobs,大家好 我一直在寻找一种使我的脚本更高效的方法,我已经得出结论(在StackOverflow上的好人的帮助下),开始工作是一条路要走 我希望在$servers中的所有服务器上同时运行以下foreach循环。我无法理解如何实际收集从接收作业返回的信息并将其添加到$serverlist PS:我知道我离把这件事搞清楚还有很长的路要走,但我真的很想得到一些帮助,因为我对如何开始工作和接受工作感到困惑 注释 我正在脚本末尾将$serverlist输出到csv文件 我在完整脚本中列出了大约500台服务器
- 我正在脚本末尾将$serverlist输出到csv文件
- 我在完整脚本中列出了大约500台服务器
$computers = gc c:\somedir\complist.txt
$n = 6
$complists = @{}
$count = 0
$computers |% {$complists[$count % $n] += @($_);$count++}
0..($n-1) |% {
start-job -scriptblock {gwmi win32_operatingsystem -computername $args} - argumentlist $complists[$_]
}
由于循环只需要处理字符串,因此很容易将其转换为并发脚本 下面是一个使循环使用后台作业来加快处理速度的示例 代码将在数组中循环并旋转后台作业,以在脚本块
$sb
中运行代码。$maxJobs
变量控制一次运行的作业数量,$chunkSize
变量控制每个后台作业将处理的服务器数量
在脚本块中添加其余的处理,并添加要返回到PsObject的任何其他属性
$sb = {
$serverInfos = @()
$args | % {
$IPAddress = [Net.Dns]::GetHostAddresses($_) | select -expand IPAddressToString
# More processing here...
$serverInfos += New-Object -TypeName PsObject -Property @{ IPAddress = $IPAddress }
}
return $serverInfos
}
[string[]] $servers = Get-QADComputer -sizelimit 500 -WarningAction SilentlyContinue -OSName *server*,*hyper* | Select -Expand Name
$maxJobs = 10 # Max concurrent running jobs.
$chunkSize = 5 # Number of servers to process in a job.
$jobs = @()
# Process server list.
for ($i = 0 ; $i -le $servers.Count ; $i+=($chunkSize)) {
if ($servers.Count - $i -le $chunkSize)
{ $c = $servers.Count - $i } else { $c = $chunkSize }
$c-- # Array is 0 indexed.
# Spin up job.
$jobs += Start-Job -ScriptBlock $sb -ArgumentList ( $servers[($i)..($i+$c)] )
$running = @($jobs | ? {$_.State -eq 'Running'})
# Throttle jobs.
while ($running.Count -ge $maxJobs) {
$finished = Wait-Job -Job $jobs -Any
$running = @($jobs | ? {$_.State -eq 'Running'})
}
}
# Wait for remaining.
Wait-Job -Job $jobs > $null
$jobs | Receive-Job | Select IPAddress
以下是每个作业处理一台服务器的版本:
$servers = Get-QADComputer -WarningAction SilentlyContinue -OSName *server*,*hyper*
# Create list
$serverlist = @()
$sb = {
param ([string] $ServerName)
try {
# Fetch IP
$ipaddress = [System.Net.Dns]::GetHostAddresses($ServerName)| select-object IPAddressToString -expandproperty IPAddressToString
# Gather OSName through WMI
$OSName = (Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName ).caption
# Ping the server
if (Test-Connection -ComputerName $ServerName -count 1 -Quiet ) {
$reachable = "Yes"
}
# Save info about server
$serverInfo = New-Object -TypeName PSObject -Property @{
SystemName = ($ServerName).ToLower()
IPAddress = $IPAddress
OSName = $OSName
}
return $serverInfo
} catch {
throw 'Failed to process server named {0}. The error was "{1}".' -f $ServerName, $_
}
}
# Loop servers
$max = 5
$jobs = @()
foreach($server in $servers) {
$jobs += Start-Job -ScriptBlock $sb -ArgumentList $server.Name
$running = @($jobs | ? {$_.State -eq 'Running'})
# Throttle jobs.
while ($running.Count -ge $max) {
$finished = Wait-Job -Job $jobs -Any
$running = @($jobs | ? {$_.State -eq 'Running'})
}
}
# Wait for remaining.
Wait-Job -Job $jobs > $null
# Check for failed jobs.
$failed = @($jobs | ? {$_.State -eq 'Failed'})
if ($failed.Count -gt 0) {
$failed | % {
$_.ChildJobs[0].JobStateInfo.Reason.Message
}
}
# Collect job data.
$jobs | % {
$serverlist += $_ | Receive-Job | Select-Object SystemName,IPAddress,OSName
}
非常感谢。开始20份工作怎么样?这可行吗?还是说我完全走错了路?20岁也许可以。当你试着运行这个程序时,它会在计算机上运行,并且会发生什么。基本上,这相当于“我的计算机现在能处理启动20多个PowerShell实例吗?”这似乎会消耗你创造工作的开销。每台服务器一个作业意味着您必须加载(和卸载)powershell.exe 500次,然后才能全部加载(和卸载)powershell.exe,而每个作业只做少量的工作(查找1台服务器的IP地址和OS名称)。@mjolinor取决于循环每次迭代所需的时间。我对它做了一些修改,将一大块服务器发送到每个后台作业。谢谢你的评论:)非常感谢!这太棒了!我在每台服务器上执行很多命令,所以我真的不需要chunksize。每个作业还生成5个csv文件。我昨晚登录并在添加chunksize部分之前查看了代码。如果没有它,代码将如何?非常感谢你!这太棒了:)@Sune我重新添加了它。您可以使用第一个并将块大小设置为1,也可以只使用原始版本。@Sune您必须将多个对象放入一个数组并返回该数组。
$servers = Get-QADComputer -WarningAction SilentlyContinue -OSName *server*,*hyper*
# Create list
$serverlist = @()
$sb = {
param ([string] $ServerName)
try {
# Fetch IP
$ipaddress = [System.Net.Dns]::GetHostAddresses($ServerName)| select-object IPAddressToString -expandproperty IPAddressToString
# Gather OSName through WMI
$OSName = (Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName ).caption
# Ping the server
if (Test-Connection -ComputerName $ServerName -count 1 -Quiet ) {
$reachable = "Yes"
}
# Save info about server
$serverInfo = New-Object -TypeName PSObject -Property @{
SystemName = ($ServerName).ToLower()
IPAddress = $IPAddress
OSName = $OSName
}
return $serverInfo
} catch {
throw 'Failed to process server named {0}. The error was "{1}".' -f $ServerName, $_
}
}
# Loop servers
$max = 5
$jobs = @()
foreach($server in $servers) {
$jobs += Start-Job -ScriptBlock $sb -ArgumentList $server.Name
$running = @($jobs | ? {$_.State -eq 'Running'})
# Throttle jobs.
while ($running.Count -ge $max) {
$finished = Wait-Job -Job $jobs -Any
$running = @($jobs | ? {$_.State -eq 'Running'})
}
}
# Wait for remaining.
Wait-Job -Job $jobs > $null
# Check for failed jobs.
$failed = @($jobs | ? {$_.State -eq 'Failed'})
if ($failed.Count -gt 0) {
$failed | % {
$_.ChildJobs[0].JobStateInfo.Reason.Message
}
}
# Collect job data.
$jobs | % {
$serverlist += $_ | Receive-Job | Select-Object SystemName,IPAddress,OSName
}