Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/76.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Powershell 使用开始作业同时运行多个脚本块(而不是循环)_Powershell_Foreach_Jobs - Fatal编程技术网

Powershell 使用开始作业同时运行多个脚本块(而不是循环)

Powershell 使用开始作业同时运行多个脚本块(而不是循环),powershell,foreach,jobs,Powershell,Foreach,Jobs,大家好 我一直在寻找一种使我的脚本更高效的方法,我已经得出结论(在StackOverflow上的好人的帮助下),开始工作是一条路要走 我希望在$servers中的所有服务器上同时运行以下foreach循环。我无法理解如何实际收集从接收作业返回的信息并将其添加到$serverlist PS:我知道我离把这件事搞清楚还有很长的路要走,但我真的很想得到一些帮助,因为我对如何开始工作和接受工作感到困惑 注释 我正在脚本末尾将$serverlist输出到csv文件 我在完整脚本中列出了大约500台服务器

大家好

我一直在寻找一种使我的脚本更高效的方法,我已经得出结论(在StackOverflow上的好人的帮助下),开始工作是一条路要走

我希望在$servers中的所有服务器上同时运行以下foreach循环。我无法理解如何实际收集从接收作业返回的信息并将其添加到$serverlist

PS:我知道我离把这件事搞清楚还有很长的路要走,但我真的很想得到一些帮助,因为我对如何开始工作和接受工作感到困惑

注释

  • 我正在脚本末尾将$serverlist输出到csv文件
  • 我在完整脚本中列出了大约500台服务器

关于启动作业,您需要了解的是,它启动Powershell的一个新实例,作为一个单独的进程运行。Receive job提供了一种机制,可以将该会话的输出拉回到本地会话中,以便在主脚本中使用它。虽然听起来很吸引人,但同时运行所有这些将意味着在您的计算机上启动500个Powershell实例,所有实例同时运行。这可能会产生一些意想不到的后果

如果有帮助的话,这里有一种划分工作的方法:

将计算机名称数组拆分为$n个数组,并使用每个数组作为脚本块的参数列表启动新作业:

  $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
}