デバッグ出力の文字色指定とISE [PowerShell]
$Host.PrivateData.VerboseForegroundColor = 'DarkGreen' $Host.PrivateData.VerboseBackgroundColor = 'Black'のように指定して詳細出力はダークグリーンに変更していました。
ところがこれを ISE の中で実行すると次のようなエラーになってしまいます。
Property 'VerboseForegroundColor' cannot be found on this object; make sure it exists and is settable.ISE内では$host が標準のコンソールとは違うものになっていて PrivateData プロパティが存在しません。
#### ISE内の $host ####ということでWindows PowerShell ISE Host での色指定の方法がわからないので、とりあえずその場合には指定をしないという安易な回避策をとっています。
Name : Windows PowerShell ISE Host
Version : 2.0
InstanceId : f80a1692-70bd-498d-96c5-1ba368660197
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : ja-JP
CurrentUICulture : ja-JP
PrivateData :
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
#### 標準コンソールの $host ####
Name : ConsoleHost
Version : 2.0
InstanceId : ca20e341-fa77-4860-9499-bb51a84fae11
UI : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture : ja-JP
CurrentUICulture : ja-JP
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
if ($Host.Name -eq 'ConsoleHost') { $Host.PrivateData.VerboseForegroundColor = 'DarkGreen' $Host.PrivateData.VerboseBackgroundColor = 'Black' }どうすれば指定できるのだろう。
フォーマットストリングと配列 Lengthの副作用? [PowerShell]
配列の長さを知るための Length や Count を使うと不思議な副作用があるようなのです。
まず配列を用意します。たとえば文字列の配列。
$a = 'Xx','Yy','Zz'これを次にようにフォーマットストリングを使って
"{0}" -f $aのように出力してみると、
"{1}" -f $a
"{2}" -f $a
Xxとなります。ここで配列の長さを知るために
Yy
Zz
$a.Lengthとすると、当然
3と出力されます。さらに先ほどと同じくフォーマットストリングを使って
"{0}" -f $aのように出力してみると、今度は
"{1}" -f $a
"{2}" -f $a
Xx Yy Zzのようにインデックス0番目を指定したところで全要素が出力され、1番目2番目はエラーになってしまいます。
Error formatting a string: インデックス (0 ベース) は 0 以上で、引数リストのサイズよりも小さくなければなりません。.
At line:13 char:9
+ "{1}" -f <<<< $a
+ CategoryInfo : InvalidOperation: (Xx Yy Zz:PSObject) [], RuntimeException
+ FullyQualifiedErrorId : FormatError
Error formatting a string: インデックス (0 ベース) は 0 以上で、引数リストのサイズよりも小さくなければなりません。.
At line:14 char:9
+ "{2}" -f <<<< $a
+ CategoryInfo : InvalidOperation: (Xx Yy Zz:PSObject) [], RuntimeException
+ FullyQualifiedErrorId : FormatError
なぜでしょう。
配列ではなくなったかのように見えます。
とはいえ [] での要素へのアクセスは可能で、たとえば
"{0} {1} {2}" -f $a[0],$a[1],$a[2]のように記述すれば、
"{0}" -f $a[0]
"{0}" -f $a[1]
"{0}" -f $a[2]
Xx Yy Zzと期待通りの動きをします。
Xx
Yy
Zz
さらに不思議なことにここで
$a += 'Aa'と追加してやるとまた元に戻って
"{0}" -f $a
"{1}" -f $a
"{2}" -f $a
でアクセスできるようになります。
PowerShellのバックグラウンドジョブ [PowerShell]
PowerShell v1 ではそれに相当する機能はありません(よね?)
v2 からは実現可能になっています。CTP2 では runspace、CTP3 では PSSession を用いて、こんな感じで書けます。
$session01 = New-PSSession -ComputerName $ComputerNameなんだか面倒ですよね。なぜなんでしょう。
$job = Invoke-Command `
-AsJob -ScriptBlock $blockTest01 `
-Session $session01
さらにこの方法には CTP2 から CTP3 になって、できるようになるかと思っていたけどまだそのままの制限があります。
バックグラウンドジョブを実行しているセッションの中からはたとえば
Get-WmiObject -Class Win32_BIOS -ComputerName $OtherComputerのように他のコンピュータにアクセスができないのです。
Enter-PSSessionn の二段重ねが出来ないのも同じ理由かもしれません。
認証とか権限とかの問題のような気がするので、クレデンシャルをうまく渡すとかhttpsでつなぐとかすればどうにかなるのかもしれませんが、winrm 周りの設定が複雑でよくわかりません。(^_^;
どうにかならないものですかねえ。。。
というフィードバックをしようと思いつつ英語で書くのが面倒で・・・
タイマーを使ってフォームの定期更新 [PowerShell]
PowerShellでWindowsフォームによるGUIを使ってみています。表示内容の定期的な自動更新を行うために、タイマーを利用した例を書いておきます。
PowerShellでのGUIとはいえ独自の世界があるわけではなく、Windowsフォームの知識があれば書けるということになっているようです。
今回試してみたいタイマーについても、MSDNの次のページに説明はあるのですが、残念ながら使用例としては- Visual Basic
- C#
- J#
- Visual C++
「Windows フォームの Timer コンポーネントを使用して一定間隔でプロシージャを実行する」
http://msdn.microsoft.com/ja-jp/library/3tszykws.aspx
ということで原理についてはわかったつもりでも、実際にPowerShellで書くには、コールバックの書き方などの文法の細かい部分を理解するのに少し試行錯誤が必要でしたので、忘れないためにもここに記録しておきます。いや、わかってしまえば単純なんですけどね。
空っぽのフォームを作って、そのタイトルに現在時刻と更新回数を表示するだけのとても単純化した例です。
[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
$timerTest = New-Object Windows.Forms.Timer
$counter = 0
$timerTestTick = {
$formInfo.text = "{0} ({1})" -f (Get-Date), $counter++
}
function ShowInfoMain
{
#=== タイマーの初期化
$timerTest.Add_Tick($timerTestTick)
$timerTest.Interval = 200
$timerTest.Enabled = $TRUE
$timerTest.Start()
#=== フォームを作って表示
$formInfo = CreateFormInfo
[void]$formInfo.ShowDialog()
}
function CreateFormInfo
{ # フォームの定義
$form = New-Object Windows.Forms.Form
# ここでその他の要素を定義する
$form
}
. ShowInfoMain
[CTP3] PowerShell ISE での日本語入力とフォント指定 [PowerShell]
$psise.Options.FontName = 'ms gothic'
\Documents and Settings\<username>\My Documents\WindowsPowerShell
\Users\<username>\Documents\WindowsPowerShell
[CTP3] uint64 の演算 [PowerShell]
The operation '[System.UInt64] - [System.UInt64]' is not defined.
PS > [uint64]$v1 = 2147483648
PS > [uint64]$v2 = 2147483647
PS > $v1 - $v2
- 1bit 精度を失っても問題のない場合には、それぞれ2で割って引いて2倍する。
- そもそも元の値がInt64の範囲を超えない保証があるならキャストして引き算する。
- Decimal構造体を使う。たとえば $GDiff = [Decimal]$G2 - [Decimal]$G1 のように。
キー入力でループ脱出 [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日に追記しました。[CTP3] Remoting 機能の設定 [PowerShell]
リリースノートの Enable Remoting の記述にしたがって
Unregister-PSSessionConfiguration * -force
Register-PSSessionConfiguration Microsoft.PowerShell -force
Enable-PSRemoting
という操作を行ったら、どれもエラーになってしまうマシンがありました。
エラーメッセージを見るとどうやら Windows Remote Management サービス
に依存しているサービスがあると、上記コマンドで -force をつけていても
サービスの停止、起動に失敗してしまうようです。
いろいろ試しているマシンだったので
System Center Virtual Machine Agent (x64)
が入っていて、そのサービスが依存してきていたのでした。
これをアンインストールすれば問題なく設定することができました。
runspace が PSSession に [PowerShell]
影響を受けたところからぼちぼちと書き残します。
runspace は PSSession に名前が変わりました。
コマンドレット名が全部 *-Runspace から *-PSSession に変更。
名前だけではなく内部の通信プロトコルも変更になったようで
CTP2 のままのマシンと CTP3 にしたマシン間はつながりません。
PowerShell 2 CTP 3 [PowerShell]
試さないと・・・
とりあえず Get-Help の表示は、
簡略な引数名の羅列だけのものしか出ませんね。
-full をつけてもそのままです。
古い環境もどこかすぐ使えるところに
残しておく方が便利そうですね。