PowerShell asynchronous timer events not working outside the test console

I have a PowerShell script that uses an asynchronous timer event (background process) to measure how long a certain condition has been met before taking the appropriate action. This works fine when I run the script inside PowerGUI, but when I run the script using a point source or run it through a batch file, the timer actions do not fire.

Here is a snippet of code.

$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> } 

So, when it works, I will see the output of "timeout: XX" every 10 seconds, written to the log. But this only happens when starting inside the editor. When I run it through a batch file, nothing happens (although I can confirm that the while loop is handling fine).

So my question is, why is my experience different when I run the script inside PowerGUI or via the command line? I think there might be a problem with viewports or parallel threads, but I'm not quite sure what the problem is. Also, I do not fire these events inside any functions or loops.

+6
source share
1 answer

When the script file is called, the $ action script block is executed using the scope of the caller (parent scope), and not the scope of the script (content area). Therefore, the variables defined in the script file are not available in the $ action script block, unless they are defined to use the global scope or source with a dot (which will make them available in the global scope). See this great article for more information.

Suppose the code below is in a file called 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 } 

When called from the powershell console, when the $ action script block is executed, the variables used will have no value.

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

If you define the variables used in the $ action script block before you invoke the script, the values ​​will be available after the action completes:

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

If you use a dot-source script, the variables defined in the script will become available in the current scope, so when the action is completed, the values ​​will be available:

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

If the variables would be declared in the global scope in the script file:

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

Then, when the $ action script block is executed in the parent scope, the values ​​will be available:

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

Source: https://habr.com/ru/post/946427/


All Articles