【问题标题】:PowerShell asynchronous timer events not working outside of testing consolePowerShell 异步计时器事件在测试控制台之外不起作用
【发布时间】:2013-05-29 21:35:53
【问题描述】:

我有一个 PowerShell 脚本,它使用异步计时器事件(后台进程)来测量在采取适当措施之前某个条件已经发生了多长时间。 当我在 PowerGUI 中运行脚本时,这工作得非常好,但是当我使用点源运行脚本或通过批处理文件运行它时,定时器事件操作不会触发。

这是一个代码 sn-p。

$timer = New-Object System.Timers.Timer
$timer.Interval = 10000  
$timer.AutoReset = $true  
$timeout = 0

$action = { 
    "timeout: $timeout" | Add-Content $loglocation
    <more stuff here>
    $timer.stop()
}  
$start = Register-ObjectEvent -InputObject $timer -SourceIdentifier TimerElapsed -EventName Elapsed -Action $action

$timer.start()

while(1)
{
    <do some testing here>
}

所以当它工作时,我会每 10 秒看到一次“超时:XX”输出写入日志。但这只有在编辑器中运行时才会发生。当我通过批处理文件运行它时,没有任何反应(尽管我可以确认 while 循环处理正常)。

所以我的问题是,为什么我在 PowerGUI 中运行脚本与通过命令行运行脚本时的体验不同?我的想法是作用域或并行线程可能存在问题,但我不确定问题是什么。我也没有在任何函数或循环中运行这些事件。

【问题讨论】:

    标签: shell powershell scripting


    【解决方案1】:

    调用脚本文件时,$action 脚本块使用调用者的范围(父范围)执行,而不是脚本文件的范围(子范围)。因此,脚本文件中定义的变量在 $action 脚本块中不可用,除非它们被定义为使用全局范围或点源(这将使它们在全局范围内可用)。请参阅this wonderful article 了解更多信息。

    假设以下代码包含在名为 test.ps1 的文件中。

    $timer = New-Object System.Timers.Timer
    $timer.Interval = 10000  
    $timer.AutoReset = $false
    
    $timeout = 100
    $location = 'SomeLocation'
    $sourceIdentifier = 'SomeIdentifier'
    
    $action = { 
    Write-Host "Timer Event Elapsed. Timeout: $timeout, Location: $location, SourceIdentifier: $sourceIdentifier"
    $timer.stop()
    Unregister-Event $sourceIdentifier
    }  
    
    $start = Register-ObjectEvent -InputObject $timer -SourceIdentifier $sourceIdentifier -EventName Elapsed -Action $action
    
    $timer.start()
    
    while(1)
    {
     Write-Host "Looping..."
     Start-Sleep -s 5
    }
    

    当从 powershell 控制台调用时,当 $action 脚本块被执行时,它使用的变量将没有值。

    ./test.ps1
    
    Timer Event Elapsed. Timeout: , Location: , SourceIdentifier:
    

    如果您在调用脚本之前定义了 $action 脚本块中使用的变量,则这些值将在操作执行时可用:

    $timeout = 5; $location = "SomeLocation"; $sourceIdentifier = "SomeSI"
    ./test.ps1
    
    Timer Event Elapsed. Timeout: 5, Location: SomeLocation, SourceIdentifier: SomeSI
    

    如果您对脚本进行点源,则脚本中定义的变量将在当前范围内可用,因此当操作执行时,值将可用:

    . ./test.ps1
    
    Timer Event Elapsed. Timeout: 100, Location: SomeLocation, SourceIdentifier: SomeIdentifier
    

    如果变量将在脚本文件的全局范围内声明:

    $global:timeout = 100
    $global:location = 'SomeLocation'
    $global:sourceIdentifier = 'SomeIdentifier'
    

    然后当 $action 脚本块在父作用域中执行时,值将可用:

    ./test.ps1
    
    Timer Event Elapsed. Timeout: 100, Location: SomeLocation, SourceIdentifier: SomeIdentifier
    

    【讨论】:

    【解决方案2】:

    就像 dugas 的回答,但如果您不想用额外的变量弄乱您的 PowerShell 实例或进行任何点源,您可以将它放在一个函数中。这还有一个好处是让您可以使用命名参数,并且如果您想在将来重新使用它,它会更加模块化。

    function Start-Timer
    {
        param($timeout = 5, $location = "SomeLocation", $sourceIdentifier = "SomeSI")
        
        $timer = [System.Timers.Timer]::new()
        $timer.Interval = $timeout
        $timer.AutoReset = $False
    
        $action = 
        { 
            $myArgs = $event.MessageData
            $timeout = $myArgs.timeout
            $location = $myArgs.location
            $sourceIdentifier = $myArgs.sourceIdentifier
            $timer = $myArgs.timer
    
            Write-Host "Timer Event Elapsed. Timeout: $timeout, Location: $location, SourceIdentifier: $sourceIdentifier"
            $timer.Stop()
            Unregister-Event $sourceIdentifier
        }
    
        # You have to pass the data this way
        $passThru = 
        @{
            timeout = $timeout;
            location = $location;
            sourceIdentifier = $sourceIdentifier;        
            timer = $timer;
        }
        Register-ObjectEvent -InputObject $timer -EventName Elapsed -SourceIdentifier Tick -Action $action -MessageData $passThru | Out-Null
    
        $timer.Start()
    }
    

    然后你可以用命名参数调用它:

    Start-Timer -location "NewLocation"
    

    纯粹使用这种方法的一个缺点是,如果 Handler 使用包含范围内的大量变量,代码会变得混乱。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-10
      • 2012-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多