PowerShell作业 vs Start-Process

4

我写了一个小的诊断脚本,保存在我的$profile里。通过获取CPU名称,我发现该命令需要约4秒钟 (Get-WmiObject -Class Win32_Processor).Name。因此,我想尝试使用PowerShell Jobs,虽然我认为它们对于长时间运行的后台作业非常好,但如果您只想在后台快速获取少量信息,则初始化时间有些麻烦(每个作业大约2-3秒),因此我打算使用Start-Process将值转储到临时文件中,而我的脚本的其余部分则继续运行。我认为我做得对,但是如果您运行此函数3或4次,您会注意到CPU名称未填充。

• 像这样使用Start-Process是否最佳? 还是有更快的并行启动小型后台作业的方法? 我知道有一种.NET方法可以做到这一点(但从我看到的内容来看,似乎非常复杂)?

• 您是否知道我的“等待文件被创建并且在访问之前为非零”经常失败的原因?

function sys {
    $System = get-wmiobject -class "Win32_ComputerSystem"
    $Mem = [math]::Ceiling($System.TotalPhysicalMemory / 1024 / 1024 / 1024)

    $wmi = gwmi -class Win32_OperatingSystem -computer "."
    $LBTime = $wmi.ConvertToDateTime($wmi.Lastbootuptime)
    [TimeSpan]$uptime = New-TimeSpan $LBTime $(get-date)
    $s = "" ; if ($uptime.Days -ne 1) {$s = "s"}
    $uptime_string = "$($uptime.days) day$s $($uptime.hours) hr $($uptime.minutes) min $($uptime.seconds) sec"

    $temp_cpu = "$($env:TEMP)\ps_temp_cpu.txt"
    $temp_cpu_cores = "$($env:TEMP)\ps_temp_cpu_cores.txt"
    $temp_cpu_logical = "$($env:TEMP)\ps_temp_cpu_logical.txt"
    rm -force $temp_cpu -EA silent ; rm -force $temp_cpu_cores -EA silent ; rm -force $temp_cpu_logical -EA silent
    Start-Process -NoNewWindow -FilePath "powershell.exe" -ArgumentList "-NoLogo -NoProfile (Get-WmiObject -Class Win32_Processor).Name > $temp_cpu"
    Start-Process -NoNewWindow -FilePath "powershell.exe" -ArgumentList "-NoLogo -NoProfile (Get-WmiObject -Class Win32_Processor).NumberOfCores > $temp_cpu_cores"
    Start-Process -NoNewWindow -FilePath "powershell.exe" -ArgumentList "-NoLogo -NoProfile (Get-WmiObject -Class Win32_Processor).NumberOfLogicalProcessors > $temp_cpu_logical"
    ""
    "Hostname:          $($System.Name)"
    "Domain:            $($System.Domain)"
    "PrimaryOwner:      $($System.PrimaryOwnerName)"
    "Make/Model:        $($System.Manufacturer) ($($System.Model))"  #     "ComputerModel:  $((Get-WmiObject -Class:Win32_ComputerSystem).Model)"
    "SerialNumber:      $((Get-WmiObject -Class:Win32_BIOS).SerialNumber)"
    "PowerShell:        $($PSVersionTable.PSVersion)"
    "Windows Version:   $($PSVersionTable.BuildVersion)"
    "Windows ReleaseId: $((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name 'ReleaseId').ReleaseId)"
    "Display Card:      $((Get-WmiObject -Class:Win32_VideoController).Name)"
    "Display Driver:    $((Get-WmiObject -Class:Win32_VideoController).DriverVersion)"
    "Display ModelDesc: $((Get-WmiObject -Class:Win32_VideoController).VideoModeDescription)"
    "Last Boot Time:    $([Management.ManagementDateTimeConverter]::ToDateTime((Get-WmiObject Win32_OperatingSystem | select 'LastBootUpTime').LastBootUpTime))"   # $(wmic OS get LastBootupTime)
    "Uptime:            $uptime_string"
    # ipconfig | sls IPv4
    Get-Netipaddress | where AddressFamily -eq IPv4 | select IPAddress,InterfaceIndex,InterfaceAlias | sort InterfaceIndex

    # Get-PSDrive | sort -Descending Free | Format-Table
    # https://dev59.com/M5bfa4cB1Zd3GeqP0vCw
    # https://www.petri.com/checking-system-drive-free-space-with-wmi-and-powershell
    # https://www.oxfordsbsguy.com/2017/02/08/powershell-how-to-check-for-drives-with-less-than-10gb-of-free-diskspace/
    # Get-Volume | Where-Object {($_.SizeRemaining -lt 10000000000) -and ($_.DriveType -eq “FIXED”) -and ($_.FileSystemLabel -ne “System Reserved”)}
    gwmi win32_logicaldisk | Format-Table DeviceId, VolumeName, @{n="Size(GB)";e={[math]::Round($_.Size/1GB,2)}},@{n="Free(GB)";e={[math]::Round($_.FreeSpace/1GB,2)}}

    # Note: -EA silent on Get-Item otherwise get an error
    while (!(Test-Path $temp_cpu)) { while ((Get-Item $temp_cpu -EA silent).length -eq 0kb) { Start-Sleep -Milliseconds 500 } }
    "CPU:               $(cat $temp_cpu)"
    while (!(Test-Path $temp_cpu_cores)) { while ((Get-Item $temp_cpu_cores -EA silent).length -eq 0kb) { Start-Sleep -Milliseconds 500 } }
    "CPU Cores:         $(cat $temp_cpu_cores)"
    while (!(Test-Path $temp_cpu_logical)) { while ((Get-Item $temp_cpu_logical -EA silent).length -eq 0kb) { Start-Sleep -Milliseconds 500 } }
    "CPU Logical:       $(cat $temp_cpu_logical)"
    rm -force $temp_cpu -EA silent ; rm -force $temp_cpu_cores -EA silent ; rm -force $temp_cpu_logical -EA silent
    "Memory:            $(Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum | Foreach {"{0:N2}" -f ([math]::round(($_.Sum / 1GB),2))}) GB"
    ""
    "Also note the 'Get-ComputerInfo' Cmdlet (has more info but slower to run)"
    ""
}
1个回答

9
要在powershell中后台运行任务,有以下三种方式:
1. 使用 Invoke-Command[3] -scriptblock { script } -asJob -computername localhost 命令,以作业形式在本地计算机上运行脚本。
2. 使用 Start-Job[2] -scriptblock { script } 命令,在后台创建一个新的powershell实例,并分配资源来启动作业。
3. 使用 Start-Process[1] powershell {script} 命令,将输出重定向到文件并启动进程,但需要确保只有一个线程读写该进程的输出。
如果您想让每个作业彼此独立地在后台运行,那么最好使用第一种或第二种选项。这两种选项不需要将输出写入文件。
Invoke-Command 命令会在新的实例中与系统建立新的会话并运行作业。
Start-Job 命令会在新的powershell实例中创建一个新的后台作业,分配资源来启动该过程。就像 Start-Process 一样,Start-Job 将在单独的 powershell.exe 实例中运行该作业。
使用 Start-Process 命令需要将标准输出重定向到文件[1]。您需要依赖磁盘性能以及读写速度的快慢。同时,还需确保不会有多个线程读取/写入此进程的输出。
推荐使用:当运行100个并发作业以获取处理器信息时,我发现 Invoke-Command 是最快的选项。但这需要您提供 -ComputerName 参数,然后需要在本地计算机上启动管理员winrm会话。如果您在创建作业时不输出作业信息,则不会花费太多时间。
在运行100个并发作业以获取相同的处理器信息时,Start-Job 和 Invoke-Command 都需要大约一秒钟的时间,并且需要额外开销。
$x = 0..100 | Invoke-Command -computername localhost -scriptblock { script } -asJob
$x | % { $_ | wait-job | out-null }
$output = $x | % { $_ | Receive-Job}
# You can run measure-object, sort-object, etc as well

[1]Start-Process

RedirectStandardOutput: 指定一个文件。该命令将进程生成的输出发送到您指定的文件中。输入路径和文件名。默认情况下,输出会显示在控制台中。

[2]Start-Job

Start-Job 命令在本地计算机上启动一个 PowerShell 后台作业。... PowerShell 后台作业运行一个命令,而不与当前会话进行交互。

[3]Invoke-Command

Invoke-Command 命令在本地或远程计算机上运行命令,并返回命令的所有输出,包括错误。... 若要在后台作业中运行命令,请使用 AsJob 参数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接