尝试多线程运行一些需要一段时间才能运行的脚本。一个例子是获取用户的最后一次登录。它检查我们所有的 DC,然后返回最近的时间。我们有相当多的 DC,而且它们是全局的,因此按顺序运行需要一段时间。
我看到了这个答案如何在不使用作业的情况下并行运行 PowerShell 脚本?
这让我开始设置运行空间并运行它,但我不确定如何恢复数据。
这是我目前所拥有的
$username = Read-Host "Enter the Users ID"
$dcs = Get-ADDomainController -Filter {Name -like "*"} | Select -expandproperty name
$Code = {
Param($username,$dc)
Get-ADUser $username | Get-ADObject -Server $dc -Properties lastLogon |
Select -Expandproperty lastLogon
}
$rsPool = [runspacefactory]::CreateRunspacePool(1,8)
$rsPool.Open()
foreach($dc in $dcs)
{
$PSinstance = [powershell]::Create().AddScript($Code).AddArgument($username).AddArgument($dc)
$PSinstance.RunspacePool = $RunspacePool
$PSinstance.BeginInvoke()
}
所以我只需要等待每项工作完成,然后捕获每项工作的结果,但我不知道该怎么做
编辑:此外,我以前也曾尝试过用作业来做到这一点,但代码实际上比正常的脚本花费的时间更长
$userName = Read-Host "Enter NTID: "
$time = 0
$dcs = Get-ADDomainController -Filter {Name -like "*"} | Select -expandproperty name
$scriptbox = {
Param($username,$dc)
Get-ADUser $username | Get-ADObject -Server $dc -Properties lastLogon | Select -Expandproperty lastLogon
}
foreach($dc in $dcs){start-Job -ScriptBlock $scriptbox -ArgumentList $username,$dc}
Get-Job | Wait-Job
Get-Job
$Data = ForEach ($Job in (Get-Job)) {
Receive-Job $Job
Remove-Job $Job
}
Foreach ($date in $Data){if($date -gt $time){$time = $date}}
$dt = [DateTime]::FromFileTime($time)
write-Host $username "last logged on at:" $dt
答案1
您正确地使用运行空间而不是*-Job
cmdlet,因为它们速度更快!
我最近发过异步Tcp扫描它利用了 Runspaces,速度非常快!可以轻松修改它以运行任何脚本块。下面是集成后您的原始脚本应该是什么样子。
警告:我无法测试以下代码。我根据使用 Active Directory cmdlet 的经验对原始脚本进行了一些更改。
# Script to run in each thread.
[System.Management.Automation.ScriptBlock]$ScriptBlock = {
$adUser = Get-ADUser -Identity $args[0] -Server $args[1] -Properties lastLogon
$result = New-Object PSObject -Property @{ 'User' = $args[0];
'DC' = $args[1];
'LastLogon' = $adUser.LastLogon; }
return $result
} # End Scriptblock
function Invoke-AsyncJob
{
[CmdletBinding()]
param(
[parameter(Mandatory=$true)]
[System.String]
# User ID
$Username
)
Import-Module -Name ActiveDirectory
$Results = @()
$AllJobs = New-Object System.Collections.ArrayList
$AllDomainControllers = Get-ADDomainController -Filter "*"
$HostRunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(2,10,$Host)
$HostRunspacePool.Open()
foreach($DomainController in $AllDomainControllers)
{
$asyncJob = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddParameters($($Username,$($DomainController.Name)))
$asyncJob.RunspacePool = $HostRunspacePool
$asyncJobObj = @{ JobHandle = $asyncJob;
AsyncHandle = $asyncJob.BeginInvoke() }
$AllJobs.Add($asyncJobObj) | Out-Null
}
$ProcessingJobs = $true
Do {
$CompletedJobs = $AllJobs | Where-Object { $_.AsyncHandle.IsCompleted }
if($null -ne $CompletedJobs)
{
foreach($job in $CompletedJobs)
{
$result = $job.JobHandle.EndInvoke($job.AsyncHandle)
if($null -ne $result)
{
$Results += $result
}
$job.JobHandle.Dispose()
$AllJobs.Remove($job)
}
} else {
if($AllJobs.Count -eq 0)
{
$ProcessingJobs = $false
} else {
Start-Sleep -Milliseconds 1000
}
}
} While ($ProcessingJobs)
$HostRunspacePool.Close()
$HostRunspacePool.Dispose()
return $Results
} # End function Invoke-AsyncJob