首页 > 解决方案 > 使用 PowerShell 调用 cmd.exe,异步生成多个实例。不要等待 cmd.exe 关闭

问题描述

我正在编写一个函数,它将为 ESXi 主机上的虚拟机建立远程 Wireshark 数据包捕获会话。

该函数将获得一个 vNIC 对象数组,然后根据每个 vNIC 的信息,将在本地使用 plink 和 Wireshark 使用 plink stdout > wireshark stdin 建立远程实时 pcap 馈送。

我决定使用 cmd.exe 来调用 plink/wireshark,因为在 powershell 中调用外部命令时管道的细微差别已经打败了我,很高兴看到另一种方法来做到这一点。

我需要像这样调用 plink/wireshark 命令:

"C:\Program Files\PuTTY\plink.exe" -batch -l root -pw PASSWORD -P 22 remotehost.com pktcap-uw --switchport 1112222 -o - |
    "C:\Program Files\Wireshark\wireshark.exe" -o "gui.window_title:VMName - Network adapter X - 1112222 - Portgroupname - VMHost" -k -i -

生成上述命令的代码如下所示:

$command = "`"$($PlinkPath)`" -batch -l $($ESXiRootCredential.Username) -pw $($ESXiRootCredential.GetNetworkCredential().password) -P 22 $($currentHost.Name) pktcap-uw --switchport $($currentvNICSwitchPortInfo.portID) -o - `| `"$($WireSharkPath)`" -o `"gui.window_title:$($currentvNICSwitchPortInfo.VM.Name) - $($currentvNICSwitchPortInfo.Name) - $($currentvNICSwitchPortInfo.PortID) - $($currentvNICSwitchPortInfo.PortgroupName) - $($currentvNICSwitchPortInfo.VMHost.Name)`" -k -i - &"

这部分脚本有效。$command问题在于使用 powershell 但异步调用,cmd.exe因此如果我传入多个 vNIC,for 循环将cmd.exe使用 $command 生成,然后立即继续为下一个 vNIC 执行相同操作,依此类推。

我尝试了几种方法的组合:

Invoke-Command
Invoke-Expression
& cmd.exe /c
等等

#Requires -Modules VMware.VimAutomation.Common

function New-RemoteVMvNICPacketCapture {
    [CmdletBinding()]
    Param(
        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [VMware.VimAutomation.Types.NetworkAdapter[]]$vNIC,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$true )]
        [System.Management.Automation.PSCredential]$ESXiRootCredential,

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$false)]
        [String]$WireSharkPath = 'C:\Program Files\Wireshark\wireshark.exe',

        [ValidateNotNullOrEmpty()]
        [Parameter(Mandatory=$false)]
        [String]$PlinkPath = 'C:\Program Files\PuTTY\plink.exe'
    )

    Begin {
        $ErrorActionPreference = 'Stop'
        Write-Debug $MyInvocation.MyCommand.Name

        # Import function needed to get switch port ID(s)
        try {
            . "$PSScriptRoot\Get-VMvNICSwitchPortInfo.ps1"
        } catch {
            Write-Error "Unable to import function Get-VMvNICSwitchPortInfo, exiting!"
            break
        }
    }

    Process {
        try {
            # Get unique list of ESXi hosts from supplied vNICs(s)
            $uniqueESXiHosts = $vNIC |
                               Sort-Object -Property {$_.Parent.VMHost} |
                               Select-Object @{N="VMHost";E={$_.Parent.VMHost}} -Unique

            foreach ($ESXiHost in $uniqueESXiHosts) {
                # Get VMHost handle from current array index
                $currentHost = $ESXiHost.VMHost

                $sshService = $currentHost |
                              Get-VMHostService |
                              Where-Object {$_.Key -eq 'TSM-SSH'}

                if ($sshService.Running -ne $true) {
                    $sshService | Start-VMHostService
                }

                if (-not (Test-NetConnection -Port 22 -ComputerName $currentHost)) {
                    Write-Error "Unable to connect to \"$currentHost\" on port 22. Skipping the following VMs: " +  + ([String]::Join(', ',($vNIC | Where-Object {$_.Parent.VMHost -eq $currentHost} | Sort-Object -Property Parent).Parent.Name))
                    break
                } else {
                    Write-Host "Able to connect to $currentHost on port 22"
                }

                foreach ($vnic in ($vNIC | Where-Object {$_.Parent.VMHost -eq $currentHost} | Sort-Object -Property Parent)) {
                    $currentvNICSwitchPortInfo = $vnic | Get-VMvNICSwitchPortInfo

                    # Create remote wireshark capture session
                    $command = "`"$($PlinkPath)`" -batch -l $($ESXiRootCredential.Username) -pw $($ESXiRootCredential.GetNetworkCredential().password) -P 22 $($currentHost.Name) pktcap-uw --switchport $($currentvNICSwitchPortInfo.portID) -o - `| `"$($WireSharkPath)`" -o `"gui.window_title:$($currentvNICSwitchPortInfo.VM.Name) - $($currentvNICSwitchPortInfo.Name) - $($currentvNICSwitchPortInfo.PortID) - $($currentvNICSwitchPortInfo.PortgroupName) - $($currentvNICSwitchPortInfo.VMHost.Name)`" -k -i - &"

                    Write-Host $command -ForegroundColor Yellow

                    Invoke-Command -ScriptBlock {& cmd.exe /c "$($command)"}
                }    

                if ($sshService.Running -eq $false) {
                    $sshService | Stop-VMHostService -Confirm:$false
                }
            }
        } catch [Exception] {
            Write-Host $_.Exception.Message
            throw "Unable to establish remote pcap"
        }
    }

    End {}
}

这应该一个接一个地产生多个 plink/wireshark 实例,而无需等待。目前它产生第一个,然后等待wireshark关闭(以及相关的cmd.exe进程),然后再继续。

标签: powershellasynchronouscmd

解决方案


推荐阅读