SSブログ
QLOOKアクセス解析

デバッグ出力の文字色指定とISE [PowerShell]

PowerShellのコンソールに Write-Debug と Write-Verbose で出力される文字列の色は初期状態ではどちらも黄色になっています。違う色にしたほうが見分けやすいと思いスクリプト内で
$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 ####
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
ということでWindows PowerShell ISE Host での色指定の方法がわからないので、とりあえずその場合には指定をしないという安易な回避策をとっています。
if ($Host.Name -eq 'ConsoleHost') { $Host.PrivateData.VerboseForegroundColor = 'DarkGreen' $Host.PrivateData.VerboseBackgroundColor = 'Black' }
どうすれば指定できるのだろう。

タグ:powershell

フォーマットストリングと配列 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
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番目はエラーになってしまいます。
なぜでしょう。
配列ではなくなったかのように見えます。

とはいえ [] での要素へのアクセスは可能で、たとえば
"{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]

unix系のシェルだと & (ampersand) で簡単に作れるバックグラウンドジョブですが、
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]

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

20080101-01.PNG

[CTP3] PowerShell ISE での日本語入力とフォント指定 [PowerShell]

PowerShell v2 CTP3 で“Graphical Windows PowerShell”から名前が変わった“Windows PowerShell Integrated Scripting Environment (ISE)”の話です。

常用するかどうかは迷うところですが、とりあえず試してみようとさわったところ、どうも MS IME の入力モードが切り替わらず日本語入力ができません。[追記:PowrShell 2.0 では治っています。ちゃんと最初から日本語入力に切り替え可能です。] まあ、コメントを英語で書いたほうがいいんじゃないのと言われればそれまでですが。。。

とりあえず、メモ帳で入力した日本語文字列を貼り付けて表示できるかどうかを試してみるとそれはうまくいきます。それどころか、その後は半角/全角キーで入力モードも切り替わり、その場での入力も可能となります。

どうやら ISE 起動後に、一度メモ帳などで入力モードを切り替えてから ISE に戻ると、その後は ISE 上でも入力モードを変更することができるようになるようです。

バグのようですね。

ついでに、フォントを変更したいと思ったのですが、メニューにはそのような項目はなく、
$psise.Options.Fontname で設定するのですね。

ISE 用のプロファイル Microsoft.PowerShellISE_profile.ps1 に

$psise.Options.FontName = 'ms gothic'

などと書いておけば指定できました。
プロファイルの置き場は
\Documents and Settings\<username>\My Documents\WindowsPowerShell

\Users\<username>\Documents\WindowsPowerShell


タグ:powershell

[CTP3] uint64 の演算 [PowerShell]

PowerShell v1 では符号なし64ビット変数 uint64 間の演算を行おうとするとエラーになります。 ただし実際に大きな数値が演算子の両側にあるときのみです。 Uint64型の変数が演算対象であっても、格納されている値が 2147483647(16進数で 7FFFFFFF)を超えていなければエラーは発生しません 。
The operation '[System.UInt64] - [System.UInt64]' is not defined. 
v2 CTP3 ではそのような制限はなく演算可能となっていますね。
PS > [uint64]$v1 = 2147483648
PS > [uint64]$v2 = 2147483647
PS > $v1 - $v2
でもエラーはでません。
CTP2 ではどうでしたっけ?まだだめだったような。 (今試せる環境が無い・・・元に戻すの面倒)
今までは
  • 1bit 精度を失っても問題のない場合には、それぞれ2で割って引いて2倍する。
  • そもそも元の値がInt64の範囲を超えない保証があるならキャストして引き算する。 
  • Decimal構造体を使う。たとえば $GDiff = [Decimal]$G2 - [Decimal]$G1 のように。
などと回避してましたが、これで面倒がなくなりますね。


タグ:powershell

キー入力でループ脱出 [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日に追記しました。
タグ:powershell

[CTP3] Remoting 機能の設定 [PowerShell]

CTP3 を使っていて気づいたところの記録です。

リリースノートの Enable Remoting の記述にしたがって
Unregister-PSSessionConfiguration * -force
Register-PSSessionConfiguration Microsoft.PowerShell -force
Enable-PSRemoting

という操作を行ったら、どれもエラーになってしまうマシンがありました。
エラーメッセージを見るとどうやら Windows Remote Management サービス
に依存しているサービスがあると、上記コマンドで -force をつけていても
サービスの停止、起動に失敗してしまうようです。

いろいろ試しているマシンだったので
System Center Virtual Machine Agent (x64)
が入っていて、そのサービスが依存してきていたのでした。

これをアンインストールすれば問題なく設定することができました。


タグ:powershell

runspace が PSSession に [PowerShell]

PowerShell 2.0 が CTP2 から CTP 3 になって変更された部分について、
影響を受けたところからぼちぼちと書き残します。

runspace は PSSession に名前が変わりました。
コマンドレット名が全部 *-Runspace から *-PSSession に変更。

名前だけではなく内部の通信プロトコルも変更になったようで
CTP2 のままのマシンと CTP3 にしたマシン間はつながりません。


タグ:powershell

PowerShell 2 CTP 3 [PowerShell]

PowerShell 2 CTP 3 が出た見たいですね。
試さないと・・・

とりあえず Get-Help の表示は、
簡略な引数名の羅列だけのものしか出ませんね。
-full をつけてもそのままです。
古い環境もどこかすぐ使えるところに
残しておく方が便利そうですね。


タグ:powershell
人気ブログランキングへ
 

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。