[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 をつけてもそのままです。
古い環境もどこかすぐ使えるところに
残しておく方が便利そうですね。
スクリプトファイルの場所 2 [PowerShell]
12月21日に書いた常套句
$myd = & { Split-Path $myInvocation.ScriptName }は、基本的にはどこでも使えるのですが、スクリプトファイルの引数定義の場面で使うには注意が必要です。
param を使って定義する引数の省略時の値にファイルのパスを入れたい場合に、
$myd = & { Split-Path $myInvocation.ScriptName }などと書きたいのですが param はスクリプトファイル内の最初の実行可能行でなくてはいけないので、
param
(
  $file1 = (Join-Path $myd 'hogehoge.xml')
)
このように記述することはできません。
したがって、
param
(
  $file1 = (Join-Path (& { Split-Path $myInvocation.ScriptName }) 'hogehoge.xml')
)
のようにその場に書く必要があります。
この長さなら一行に書くものとしてなんとか許せる範囲かなと思いつつ、
なんとかもっといい方法はないものかとも・・・
Live Mesh Remote Desktop の proxy 経由での利用 [Windows Live]
Live Mesh のファイル同期が便利そうなので使ってみています。
ついでにというわけで、リモートデスクトップも試したのですが、
会社のLAN上のマシンから外部のマシンにはつながりません。
どうせ、会社のマシンから外部につなぐのはいろいろ規則的にも面倒そうなので、
実際には使わないだろうから、いいといえばいいのですが、
やはりなぜなのか気になるな・・・ということで調べてみたら、
どうやらインターネット オプションでプロキシ サーバーを
自動構成スクリプトを使用するようになっていると接続できないようです。
プロキシが不要なら切る。
使わざるを得ないなら手動設定にする。
ということで回避できています。
だけど、修正してほしいところですね。
XP Home にもリモート接続できるから便利だし。
スクリプトファイルの場所 [PowerShell]
PowerShell スクリプトの実行中に、そのファイルと同じフォルダに置かれた他のファイルにアクセスしたい場合があります。たとえば、スクリプトを複数ファイルに分割して記述していて、実行中に他のファイルをドットソーシングで読み込む場合です。もちろん他のファイルの場所を自由に配置可能として、起動パラメターでその場所を指定したりするような設計も可能ですが、その場合ですら初期値としてはすべての関連スクリプトファイルを同じフォルダに置くことにするのが便利でしょう。そのためには、スクリプト内で自ファイルの置かれている場所を取得する必要があります。
ここで必要となる、スクリプトの呼び出し関連の情報は自動変数 $myInvocation に格納されています。ただし呼び出し方とスクリプト内の位置に応じて格納されている情報の内容が変わるように見えて、最初は使い方がわかりにくかったので忘れないように書き留めておきます。
結論を先に述べてしまうと、自ファイルの場所の取得には次のような記述が便利です。これで変数 myd に実行中のスクリプトファイルの場所が取得できます。
$myd = & { Split-Path $myInvocation.ScriptName }
ここから、なぜこのようにスクリプトブロックを用いて少しばかり面倒な記述をするのかを説明します。
$myInvocation に格納されるスクリプト起動情報
スクリプトの起動に関する情報は自動変数 $myInvocation に格納されています。$myInvocation は System.Management.Automation.InvocationInfo 型であり、次のようなプロパティを持っています。
$myInvocation のプロパティ
CommandLineParameters (Version 2 から)
CommandOrigin (Version 2 から)
ExpectingInput (Version 2 から)
InvocationName
Line
MyCommand
OffsetInLine
PipelineLength
PipelinePosition
PositionMessage
ScriptLineNumber
ScriptName
UnboundArguments (Version 2 から)
ScriptName にはスクリプトのフルパス名が格納されます。したがって、これを Split-Path で分解すればスクリプトの置かれている場所を取得することができます。ただし、それは何らかの関数またはスクリプトブロックの中で取得された場合に限ります。どの関数やスクリプトブロックの中にも入らない一番外側のレベルで呼ばれた場合には空になります。また、ドットソーシングで他のスクリプトから呼ばれたスクリプト内の場合、一番外側のレベルでは ScriptName に呼び出した側のスクリプトファイルのフルパス名が入っています。
したがって、そのコードを記述する位置を気にせずいつも望みの結果を得るためには、一段スクリプトブロックの中に入ってScriptNameを取得する、前述の常套句が便利となるのです。
実は、ScriptNameには $myInvocation にアクセスしたコード上の位置を呼び出した場所のスクリプトファイルの名前が入っているという意味では一貫しています。ScriptNameの取得場所と呼び出し方で分類してみると次のようになります。(この説明での“関数”には“スクリプトブロック”も含みます。PowerShellではスクリプトブロックは名前の無い関数と考えられます。)
- [取得場所] スクリプトファイルの一番外側レベル
[取得内容] スクリプトファイルを呼び出したスクリプトファイルのフルパス名- [呼び出し方] コマンドラインからスクリプトファイルA.ps1を呼ぶ
[取得内容] 呼び出したスクリプトファイルが無いので空白 - [呼び出し方] スクリプトファイルB.ps1からA.ps1を呼ぶ
[取得内容] 呼び出したのはB.ps1なのでそのフルパス名
- [呼び出し方] コマンドラインからスクリプトファイルA.ps1を呼ぶ
- [取得場所] 関数内
[取得内容] 関数を呼び出したスクリプトファイルのフルパス名。呼び出される関数が定義された場所がどのスクリプトファイルであるのかは関係ない。- [呼び出し方] コマンドラインからスクリプトファイルA.ps1内で定義された関数を呼ぶ
[取得内容] 呼び出したスクリプトファイルが無いので空白 - [呼び出し方] スクリプトファイルA.ps1から同じファイル内で定義された関数を呼ぶ
[取得内容] 呼び出したのはA.ps1なのでそのフルパス名 - [呼び出し方] スクリプトファイルB.ps1からA.ps1内で定義された関数を呼ぶ
[取得内容] 呼び出したのはB.ps1なのでそのフルパス名
- [呼び出し方] コマンドラインからスクリプトファイルA.ps1内で定義された関数を呼ぶ
動作を確かめるためのサンプルコードや、$myInvocationの他のプロパティについても後日書きたいと思っています。