无法将脚本块传入ForEach-Object与Invoke-Command循环的问题
解决PowerShell中ForEach-Object -Parallel传递命令字符串到Invoke-Command的问题
问题原因
你的代码存在两层脚本块嵌套:ForEach-Object -Parallel的并行块,以及Invoke-Command的远程执行块。$using:作用域只能穿透一层脚本块边界,直接在远程脚本块里用$using:s会因为作用域无法识别而报错;而直接写{$s}的话,远程会话里不存在s变量,所以无报错但不会执行任何操作。
修正后的代码(方案一:通过参数传递)
Write-Host "Enter your administrative credentials" -ForegroundColor Blue $credential = Get-Credential '' $s = Read-Host "Enter your cmd script here" $complist = Get-Content "C:\PowershellCMDs\Temp\searchresults.txt" $complist | ForEach-Object -Parallel { Invoke-Command -ComputerName $_ -Credential $using:credential -ScriptBlock { param($command) # 针对cmd命令,用cmd /c执行 cmd /c $command } -ArgumentList $using:s } -ThrottleLimit 16 Write-Host "Task Complete" -BackgroundColor Green
修正后的代码(方案二:中间变量中转)
Write-Host "Enter your administrative credentials" -ForegroundColor Blue $credential = Get-Credential '' $s = Read-Host "Enter your cmd script here" $complist = Get-Content "C:\PowershellCMDs\Temp\searchresults.txt" $complist | ForEach-Object -Parallel { # 先把外层的$s通过$using:拿到并行块本地 $localCmd = $using:s Invoke-Command -ComputerName $_ -Credential $using:credential -ScriptBlock { # 再从并行块本地传递到远程会话 cmd /c $using:localCmd } } -ThrottleLimit 16 Write-Host "Task Complete" -BackgroundColor Green
关键说明
- 因为你输入的是cmd命令(比如
sfc /scannow),所以远程脚本块里需要用cmd /c来执行,否则PowerShell会把命令当作PS命令解析,可能出现错误。 - 方案一通过
Invoke-Command的-ArgumentList参数传递命令字符串,远程脚本块用param接收后执行,是最稳妥的跨会话参数传递方式。 - 方案二先在并行块内把外层变量存入本地变量,再通过
$using:传递到远程会话,适合多层嵌套场景。
后续跟踪执行进度的思路
可以通过PowerShell作业来跟踪每台机器的命令执行状态,示例代码如下:
Write-Host "Enter your administrative credentials" -ForegroundColor Blue $credential = Get-Credential '' $s = Read-Host "Enter your cmd script here" $complist = Get-Content "C:\PowershellCMDs\Temp\searchresults.txt" # 创建并行作业 $jobs = $complist | ForEach-Object -Parallel { $localCmd = $using:s $localCred = $using:credential # 为每个机器创建独立作业,命名便于识别 Start-Job -Name "Cmd_$_" -ScriptBlock { param($computer, $cred, $command) try { $result = Invoke-Command -ComputerName $computer -Credential $cred -ScriptBlock { cmd /c $command 2>&1 } return @{Computer = $computer; Status = 'Success'; Output = $result} } catch { return @{Computer = $computer; Status = 'Failed'; Error = $_.Exception.Message} } } -ArgumentList $_, $localCred, $localCmd } -ThrottleLimit 16 # 实时跟踪作业进度 do { $runningCount = ($jobs | Where-Object {$_.State -eq 'Running'}).Count $completedCount = ($jobs | Where-Object {$_.State -in 'Completed', 'Failed'}).Count Write-Host "Progress: $completedCount completed / $runningCount running" Start-Sleep -Seconds 3 } while ($runningCount -gt 0) # 接收所有作业结果 $allResults = $jobs | Receive-Job -Wait -AutoRemoveJob # 输出结果 $allResults | Format-Table Computer, Status, Output, Error -AutoSize
内容的提问来源于stack exchange,提问作者Simplejack
相关产品推荐
相关产品推荐

