キー入力でループ脱出 [PowerShell]
PowerShell で無限ループ内で何かを処理をし続け、キーボード入力でそれを中断させるようにしたいと思いました。キー入力を待ち受けるのではなくノンブロッキング動作のキー入力検出です。
"PowerShell in Action"の 11.2.3 でWebサーバーのリクエスト処理ループの例として次のようなどのキーを押しても中断できる例が載っています。(p.367)
while($true)
{
if ($host.ui.rawui.KeyAvailable)
{
write-host "Stopping server..."
break
}
普通はこれで用が足りるのです。しかし、リモート デスクトップ接続して使っている場合には、リモート デスクトップ画面を最小化したりすると、その時に送られてくる何かのイベントもここで検出されて、意図せず中断されてしまいます。
したがって、検出後にその内容を判断して選別する必要があります。VirtualKeycode の値と ControlKeyStates の値の組み合わせでどのキーを受け付けるかを決めればいいはずです。
処理は単純なのですが、まとめて書いてくれているところがないのでMSDNライブラリのいろいろな場所の情報を継ぎ合わせて読んで理解する必要がありました。書きたいことがはっきりしてからも、MSDNライブラリにはPowerShell での記述サンプルがないこともあり、実際にどういう字面で書けばいいのかわかるまで少し手間取ってしまったので忘れないように記録しておこうと思ったわけです。
たとえば次のように書くと Ctrl-t で止められます。
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$loop = 0
"Please press 'Ctrl-t' to stop."
$rawui = $Host.ui.rawui
$keystates = [System.Management.Automation.Host.ControlKeyStates]
$modifier = $keystates::LeftCtrlPressed -bor $keystates::RightCtrlPressed
$keymap = [System.Windows.Forms.Keys]
for ($termination = $FALSE; $termination -eq $FALSE; )
{
while ($rawui.KeyAvailable)
{
$keyinput = $rawui.Readkey("NoEcho,IncludeKeyUp,IncludeKeyDown")
if (($keyinput.VirtualKeycode -eq $keymap::T) -and `
($keyinput.ControlKeyState -band $modifier))
{
$termination = $TRUE
break
}
}
$loop++
"Running (" + $loop + ")"
Start-Sleep 1
}
Readkeyの引数で IncludeKeyUp と IncludeKeyDown の両方を指定していますが、これを IncludeKeyDown のみにすると、キーの上げ下げと KeyAvailable での判定と Readkey での読み出しの対応関係がくずれて(キー上げ情報が溜まって?)止まってしまいます。入力がコンソールにエコーされてもいいならばまったく引数を渡さないで呼べばいいのですが、NoEcho で抑制したい場合にはIncludeKeyUp と IncludeKeyDown の少なくとも一方を指定する必要があります。IncludeKeyUp のみを指定するか、両方を指定すると詰まらずに動作します。
それと、この例では実は Ctrl-Alt-t でも受け付けてしまいます。もっと厳密な判定が必要な場合にはもう少し論理式を複雑にしないといけません。
また Ctrl-c, Ctrl-s などは対象にできません。素直にここまで情報が届くキーのみが使えます。(ReadKeyで AllowCtrlC を指定すれば Ctrl-c は使えるのかな?)
Ctrl-Cの件2009年6月10日に追記しました。
コメント 0