【问题标题】:Powershell Script Running SlowlyPowershell脚本运行缓慢
【发布时间】:2014-06-11 15:33:55
【问题描述】:

我正在编写一个脚本来检查大约 15 台远程服务器上的版本,该脚本的执行时间比我预期的要长。

$listServers = @("compName1", "compName2", "compName3", ... "compName15")

"" | Out-File C:\temp\javaVersion.txt
"" | Out-File C:\temp\javaVersionLog.txt
$cred = Get-Credential

ForEach ($server in $listServers) 
{
     Measure-Command {$javaVersion = Invoke-Command -ComputerName $server -Credential $cred -Authentication Kerberos -ScriptBlock {Get-WmiObject -Class Win32_Product -Filter "Name like 'Java [0-9]%'" | Select -ExcludeProperty Version}} -ErrorAction SilentlyContinue -ErrorVariable errorOutput
     $errorOutput | Out-File C:\temp\javaVersionLog.txt -Append
     $server + $javaVersion | Out-File C:\temp\javaVersion.txt -Append
 }

根据 Measure-Command 输出,这需要大约 21 秒才能完成。我错过了脚本需要这么长时间才能完成的原因吗?

编辑:

在被其他问题分心后,我终于完成了剧本。

Start-Transcript C:\temp\javaVersion.txt
$listServers = @("compName1", "compName2", "compName3", ... "compName15")
$javaVersVerbose = ""

Invoke-Command -ComputerName $listServers -ScriptBlock {
    $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $_);
    $javaKey = $registry.OpenSubKey('SOFTWARE\JavaSoft\Java Runtime Environment');
    $javaVers = $javaKey.GetValue('CurrentVersion');
    $javaVersVerbose = $javaKey.GetValue('Java' + $javaVers.Substring(2, 1) + 'FamilyVersion');
    $nameKey = $registry.OpenSubKey('SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName');
    $name = $nameKey.GetValue('ComputerName');
    $name + " " + $javaVersVerbose | echo
} -ErrorAction SilentlyContinue -ErrorVariable errorOutput

$errorOutput | echo

Write-Host -NoNewLine 'Press any key to continue...'
$null = $Host.UI.RawUI.ReadKey('NoEcho, IncludeKeyDown')

【问题讨论】:

  • 原因是 Win32_Product。 ) 不,说真的。我想这是一个庞大的课程,这就是为什么查询总是那么慢的原因。在 PS 3.0 中,您可以使用 workflows 并行执行脚本块。或者(或除此之外)如果您只需要版本号,您可以使用 Get-Item 查询注册表。它要快得多。

标签: performance powershell powershell-3.0 remote-server powershell-remoting


【解决方案1】:

您不需要循环执行此操作,也不需要连续执行此操作。 invoke-command 接受ComputerNames 的集合,可以并行运行请求。

$listServers = @("compName1", "compName2", "compName3", ... "compName15")
Invoke-Command -throttlelimit 4 -ComputerName $listServers -Credential $cred -Authentication Kerberos -ScriptBlock {Get-WmiObject -Class Win32_Product -Filter "Name like 'Java [0-9]%'" | Select -ExcludeProperty Version}} -ErrorAction SilentlyContinue -ErrorVariable errorOutput

但是,正如 Tim Ferrell 所指出的,您可以使用 Get-WMIObject 远程 ping 服务器,如果您将其作为一项工作执行,它将并行运行多个请求。

Get-WMIObject Win32_Product  -Filter "Name like 'Java [0-9]%'" -computername $listServers -throttlelimit 4 -asjob |select -excludeproperty version

然后使用 Job cmdlet 接收结果。

【讨论】:

    【解决方案2】:

    有几种方法可以提高性能。 PowerShell WorkFlow 支持并行 ForEach,这将同时访问每台计算机。您还可以使用 Get-WMIObject 和 -ComputerName 来查询计算机列表。 Get-WMIObject 还支持 -AsJob 开关,这也有帮助。

    【讨论】:

      【解决方案3】:

      是的,我认为使用工作是要走的路。 WMI 调用可能需要很长时间,尤其是当您遇到一两个没有响应的主机时。

      也许可以考虑这样的事情:

      $listServers = @((1..15 | % {"compName$_"}))
      $jobList = @()
      
      $JavaBlock = {
      function checkServer ($serverName) {
          $returnValue = Get-WmiObject -computerName $serverName -Class Win32_Product -Credential $cred -Filter "Name like 'Java [0-9]%'" | Select -ExcludeProperty Version
          return $returnValue
      }
      
      }
      
      foreach ($server in $listServer) {
          $job = start-job -InitializationScript $JavaBlock -ScriptBlock { checkServer $args } -argumentList $hostname
          $jobList += $job
          while (($jobList | where { $_.state -eq "Running" }).count -ge 30) { start-sleep -s 1 }
      }
      
      while (($jobList | | where { $_.state -eq "Running" }).count -ge 1) { start-sleep -ms 500 }
      

      两个 while 语句控制作业流程。 foreach 语句中的那个会限制作业,以便一次只运行 30 个。最后一个只是等待所有工作完成后再完成。

      要收集你的结果,你可以使用这个:

      $jobList | % { $jobResults = Receive-Job $_; write-host $jobResults.result }
      

      Job 对象还有其他可能值得探索的属性。

      【讨论】:

        【解决方案4】:

        针对Win32_Product 的查询会触发reconfiguration of installed packages,这使得该过程如此耗时。它还会进行此类查询potentially harmful,因为它可能会无意中重置配置参数。

        对于特定程序的版本检查,您最好直接从(远程)注册表中读取信息:

        $listServers | % {
          $reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $_)
          $key = $reg.OpenSubKey('SOFTWARE\JavaSoft\Java Runtime Environment')
          New-Object -Type PSObject -Property @{
            'Server'      = $_
            'JavaVersion' = $key.GetValue('CurrentVersion')
          }
        } | Export-Csv 'C:\temp\javaVersion.csv' -NoType
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-10-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-06
          • 2011-12-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多