9

非同期タイマー イベント (バックグラウンド プロセス) を使用して、適切なアクションを実行する前に特定の状態が発生している時間を測定する PowerShell スクリプトがあります。これは、PowerGUI 内でスクリプトを実行すると問題なく動作しますが、ドット ソースを使用してスクリプトを実行したり、バッチ ファイルを介してスクリプトを実行したりすると、Timer Event アクションが起動しません。

コードスニペットを次に示します。

$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 秒ごとに "timeout: XX" という出力がログに書き込まれます。ただし、これはエディター内で実行した場合にのみ発生します。バッチ ファイル経由で実行しても何も起こりません (ただし、while ループが正常に処理されていることは確認できます)。

私の質問は、PowerGUI 内でスクリプトを実行している場合と、コマンド ライン経由でスクリプトを実行している場合で、なぜ私の経験が異なるのでしょうか? 私の考えでは、スコープまたは並列スレッドに問題がある可能性がありますが、問題が何であるかは正確にはわかりません。また、関数やループ内でこれらのイベントを実行していません。

4

2 に答える 2

8

スクリプト ファイルを呼び出すと、$action スクリプト ブロックは、スクリプト ファイルのスコープ (子スコープ) ではなく、呼び出し元のスコープ (親スコープ) を使用して実行されます。したがって、スクリプト ファイル内で定義された変数は、グローバル スコープまたはドット ソースを使用するように定義されていない限り (グローバル スコープで使用可能になります)、$action スクリプト ブロック内では使用できません。詳細については、このすばらしい記事を参照してください。

次のコードが 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
于 2013-06-03T17:58:04.383 に答える