【问题标题】:Output parameter from job when done完成时从作业输出参数
【发布时间】:2018-10-31 08:53:19
【问题描述】:

更新 我没有为这个错误而苦苦挣扎An event with the name 'StateChanged' does not exist - 请参阅下面的 TRYOUTS(最后一段代码)中的完整代码


原文

如前所述,我正在编写用于自动格式化文件并将文件传输到多个 USB 拇指驱动器的代码。 它是并行/异步完成的

现在的问题是我想在每个 USB 完成/工作完成时输出它们的驱动器号。该字母存储在作业函数内的 var/param 中,但我不知道如何在作业完成后将其写出来。

我有一个Register-ObjectEvent,它会在每个 USB 完成格式化和传输时触发。 该脚本运行良好,并写出了USB slot = Job...

这一行

但我想将我的$driveLetter 内部的$formatDrive 写在USB slot = $driveLetter 行中。

这是我的代码。查找$formatDrive$jobEvent 行。

#Requires -version 2.0
ipmo storage
Register-WmiEvent -Class win32_VolumeChangeEvent -SourceIdentifier volumeChange

$formatDrive = {
    Param($driveLetter)
    Write-Host (Get-Date -Format s) "Erase disk..."
    $source = "C:\Users\myname\Desktop\test"
    Format-Volume -DriveLetter $driveLetter[0] -NewFileSystemLabel "test30" -FileSystem exFAT -Confirm:$false
    robocopy $source $driveLetter /S
    return $driveLetter
}

Write-Host (Get-Date -Format s) " Beginning script..."
do {
    $newEvent = Wait-Event -SourceIdentifier volumeChange
    $eventType = $newEvent.SourceEventArgs.NewEvent.EventType
    $eventTypeName = switch ($eventType) {
        1 {"Configuration changed"}
        2 {"Device arrival"}
        3 {"Device removal"}
        4 {"docking"}
    }
    if ($eventType -eq 2) {
        $driveLetter = $newEvent.SourceEventArgs.NewEvent.DriveName
        $driveLabel = ([wmi]"Win32_LogicalDisk='$driveLetter'").VolumeName
        Write-Host (Get-Date -Format s) "USB igang = " $driveLetter
        # Execute process if drive matches specified condition(s)
        $formatDrivejob = Start-Job -ScriptBlock $formatDrive -ArgumentList $driveLetter
        $jobEvent = Register-ObjectEvent $formatDrivejob StateChanged -Action {
            Write-Host (Get-Date -Format s) (' USB slot = Job #{0} ({1}) complete.' -f $sender.Id, $sender.Name)
            [media.SystemSounds]::("Hand").Play()
            $jobEvent | Unregister-Event
        }
    }
    Remove-Event -SourceIdentifier volumeChange
} while (1-eq1) #Loop until next event
Unregister-Event -SourceIdentifier volumeChange

我试过把它写在$formatDrive 的末尾——因为它在后台运行,所以没有用。 还尝试了return,正如您在$formatDrive中看到的那样

试训:

#Requires -version 2.0
ipmo storage
Register-WmiEvent -Class win32_VolumeChangeEvent -SourceIdentifier volumeChange

$formatDrive = {
    param($driveLetter)
    write-host (get-date -format s) "Erase disk..."
    $source = "C:\Users\jbh\Desktop\test"
    Format-Volume -Driveletter $driveLetter[0] -NewFileSystemLabel "test30" -FileSystem exFAT -Confirm:$false
    robocopy $source $driveLetter /S
    return $driveLetter
}

write-host (get-date -format s) " Beginning script..."
do{
$newEvent = Wait-Event -SourceIdentifier volumeChange
$eventType = $newEvent.SourceEventArgs.NewEvent.EventType
$eventTypeName = switch($eventType)
{
1 {"Configuration changed"}
2 {"Device arrival"}
3 {"Device removal"}
4 {"docking"}
}
# initialize the array
$formatDrivejob = @()
if ($eventType -eq 2) {
    $driveLetter = $newEvent.SourceEventArgs.NewEvent.DriveName
    $driveLabel = ([wmi]"Win32_LogicalDisk='$driveLetter'").VolumeName
    Write-Host (Get-Date -Format s) "USB igang = " $driveLetter
    # Execute process if drive matches specified condition(s)
    $formatDrivejob += Start-Job -ScriptBlock $formatDrive -ArgumentList $driveLetter
    $jobEvent = Register-ObjectEvent $formatDrivejob StateChanged -Action {
        Write-Host (Get-Date -Format s) (' USB slot = Job #{0} ({1}) complete.' -f $sender.Id, $sender.Name)
        [media.SystemSounds]::("Hand").Play()
        foreach ($i in $formatDrivejob){
           if ($i.State -eq "Completed")
           {
             $letter = $formatDrivejob | receive-job | select -Last 1
             Write-Host "Drive has finished:" $letter
             $formatDrivejob.Remove($i)
           } 
        }
        $jobEvent | Unregister-Event
    }
}
Remove-Event -SourceIdentifier volumeChange
} while (1-eq1) #Loop until next event
Unregister-Event -SourceIdentifier volumeChange

【问题讨论】:

    标签: powershell


    【解决方案1】:

    您可以使用Receive-job获取结果:

    # initialize the array
    [System.Collections.ArrayList]$formatDrivejob
    if ($eventType -eq 2) {
        $driveLetter = $newEvent.SourceEventArgs.NewEvent.DriveName
        $driveLabel = ([wmi]"Win32_LogicalDisk='$driveLetter'").VolumeName
        Write-Host (Get-Date -Format s) "USB igang = " $driveLetter
        # Execute process if drive matches specified condition(s)
        $formatDrivejob += Start-Job -ScriptBlock $formatDrive -ArgumentList $driveLetter
        $jobEvent = Register-ObjectEvent $formatDrivejob StateChanged -Action {
            Write-Host (Get-Date -Format s) (' USB slot = Job #{0} ({1}) complete.' -f $sender.Id, $sender.Name)
            [media.SystemSounds]::("Hand").Play()
            foreach ($i in $formatDrivejob){
               if ($i.State -eq "Completed")
               {
                 $letter = $formatDrivejob | receive-job | select -Last 1
                 Write-Host "Drive has finished:" $letter
                 $formatDrivejob.Remove($i)
               } 
            }
            $jobEvent | Unregister-Event
        }
    }
    

    【讨论】:

    • 谢谢!!但请参阅 DarkLite1 的评论。显然我不能使用wait job,因为它会停止在作业运行时触发插入新 USB 的脚本表单。我需要同时做多项工作
    • 你是对的。但它也可以在没有等待工作的情况下工作。我更新了答案
    • @S.Spieker 是的,但您可能会尝试检索尚不存在的作业结果,即当作业未完成时。最佳做法是等待作业完成或同时启动它们并在它们全部完成后处理作业输出。
    • 是的!这似乎很有希望。但是当我插入三个 USB 时,它会首先输出 USB igang = K:UBS igang = F:USB igang = J:..... 但是只有最后一个会给出最终结果,例如:Drive has finished:Drive has finished:Drive has finished: J:
    • 确实,我也更新了我的答案,我认为我会怎么做。无论如何,这两种方法都应该有效。
    【解决方案2】:

    也许你可以试试这样的方法:

    $DrivesToHandle = 5
    $formatDrivejob = @()
    
    Do {
        if ($eventType -eq 2) {
            $DrivesToHandle -= 1
            $driveLetter = $newEvent.SourceEventArgs.NewEvent.DriveName
            $driveLabel = ([wmi]"Win32_LogicalDisk='$driveLetter'").VolumeName
            write-host (Get-Date -format s) "USB igang = $driveLetter" 
            # Execute process if drive matches specified condition(s)
            $formatDrivejob += Start-Job -ScriptBlock $formatDrive -ArgumentList $driveLetter
        }
    } while ($DrivesToHandle -ne 0)
    
    # Wait for all jobs to finish
    $formatDrivejob | Wait-Job | Out-Null
    
    # Retrieve the job results
    $JobResults = $formatDrivejob | Receive-Job
    
    foreach ($Job in $JobResults) {
        Write-Host (get-date -format s) "Job result of job ID $($Job.Id): " $Job
    }
    
    # Remove all jobs
    $formatDrivejob | Remove-Job -Force
    

    当您使用jobs 时,您可以使用Wait-Job 等待工作完成。但是您还必须检索作业结果,这是通过使用Receive-Job CmdLet 完成的。

    【讨论】:

    • 谢谢!但是对于Wait-Job,我不能一次做多个USB。如果我在代码中有Wait-Job,我不会在工作完成之前触发插入的新 USB
    • 是的,您可以取出等待并检索结果的部分,然后在所有作业开始后在循环之后执行此操作。然后,您可以一次检索所有作业结果。不要忘记将$formatDrivejob = Start-Job 更改为$formatDrivejob += Start-Job 以获得可以迭代的array 工作结果。
    • 谢谢!但它似乎永远不会退出 while 循环,所以我不知道在哪里放置 Wait-Job
    • 谢谢!我喜欢这个。但是由于您必须定义要处理的设备数量,我想我会选择 Spikers 解决方案 :)
    猜你喜欢
    • 2019-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    • 2017-11-24
    • 2019-09-06
    • 1970-01-01
    相关资源
    最近更新 更多