Saturday, July 19, 2008

前回はステップ実行について説明しました。

今回はブレークポイントを設定する方法について紹介します。

ブレークポイントの設定は、以外と簡単で $host.EnterNestedPrompt() をブレークポイントをかけたい場所に記述するだけです。

以前に紹介したスクリプトにブレークポイントを設定して実験してみたいと思います。(5行目に挿入しました。スクリプトはC:\Work\Debug.ps1として保存します)

$a = 2
$b = 3
$c = $a + $b
$host.EnterNestedPrompt()   #ブレークポイントの設定
Write-Debug "Line4: $c"
$a = 4
$b = 5
$c = $a + $b
Write-Debug "Line8: $c"

上記を実行してみます。

PS C:\Work> ./Debug.ps1
デバッグ: Line4: 5
PS C:\Work>>>

上記のように、ブレークポイントを設定した位置までコードが実行されプロンプトが表示されたと思います。

この状態は、前回のステップ実行で説明した「中断処理」と同様で、変数の状態を確認することが可能です。

試しに $a, $b, $c がどうなっているかみてみましょう。

PS C:\Work> ./Debug.ps1
デバッグ: Line4: 5
PS C:\Work>>> $a
2
PS C:\Work>>> $b
3
PS C:\Work>>> $c
5
PS C:\Work>>>

変数の値を確認できました。プロンプトを終了するには exitと入力して[Enter]キーを押します。

PS C:\Work>>> exit
デバッグ: Line8: 9
PS C:\Work>

 

ブレークポイントを設定するのが簡単だということをおわかりいただけたでしょうか?

是非使ってみてください。

Saturday, July 19, 2008 11:31:02 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Friday, July 18, 2008

Set-PSDebug コマンドレットの-Stepパラメータを使用すると、ステップ実行を行うことができます。

以前にも使用したことのある下記スクリプトで実験してみましょう。

下記スクリプトをメモ帳などで入力してC:\Work\Debug.ps1 に保存します。

$a = 2
$b = 3
$c = $a + $b
Write-Debug "Line4: $c"
$a = 4
$b = 5
$c = $a + $b
Write-Debug "Line8: $c"

以下の一文を入力し[Enter]を押すとステップ実行が開始されます。

PS C:\Work> Set-PSDebug -step; ./Debug.ps1

1行実行されるごとにメッセージが表示されます。

PS C:\Work> Set-PSDebug -step; ./Debug.ps1

この操作を続行しますか?
   1+ Set-PSDebug -step; ./Debug.ps1
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ
(既定値は "Y"):
デバッグ:    1+ Set-PSDebug -step; ./Debug.ps1

「規定は"Y"」と憑依されている箇所がありますが、これは[Enter]キーを押すと次の行が押される、つまり[Y]キーを押すのと同じ操作を行うこと意味します。

とりあえず[Enter]を押していって、下記のような状態になったらいったん止まってください。

PS C:\Work> Set-PSDebug -step; ./Debug.ps1

この操作を続行しますか?
   1+ Set-PSDebug -step; ./Debug.ps1
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ
(既定値は "Y"):
デバッグ:    1+ Set-PSDebug -step; ./Debug.ps1

この操作を続行しますか?
   1+ $a = 2
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ
(既定値は "Y"):
デバッグ:    1+ $a = 2

この操作を続行しますか?
   2+ $b = 3
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ
(既定値は "Y"):

この状態で下記のように「中断」を意味する[S]キーを入力してみましょう。

この操作を続行しますか?
  2+ $b = 3
Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ
既定値は "Y"):S

[S]を入力すると、下記のように PS C:\Work>>> というプロンプトが表示されたと思います。ここで $a[Enter] と入力してみてください。

PS C:\Work>>> $a
2

2という値が表示されたと思います。このように処理を中断して変数の中身を確認することが可能です。

プロンプトを終了するには exit と入力します。

Visual Studio.NETのように現在ステップ実行している行がハイライトされるわけではないので見にくく感じるかもしれませんが、慣れてしまえば非常に有効なデバッグ手段です。

是非ためしてみることをおすすめします。

Friday, July 18, 2008 11:19:28 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Thursday, July 17, 2008

Set-PSDebugコマンドレットによるデバッグ(PowerShell 入門) でSet-PSDebugコマンドレットの使用方法について説明しました。

今回は、このコマンドレットの-Strictパラメータを使用して、未定義の変数をエラーとして扱ってみましょう。

下記スクリプトで実験してみたいと思います。

ファイル名 Strict.ps1 とし C:\Work に保存したものとします。

1 * $a

上記スクリプトの中の$aはどこでも定義されていない状態で、いきなり使用されています。

これをSet-PSDebugコマンドレットで-Strictパラメータを使用して実行してみましょう。

PS C:\Work> Set-PSDebug -Strict; ./Strict.ps1
変数 $a は、設定されていないために取得できません。
発生場所 C:\Work\Strict.ps1:1 文字:7
+ 1 * $a <<<<

このように-Strictパラメータを使用すると、未定義の変数があることがわかります。

では、先ほどのスクリプトを修正して、変数$aを定義された状態で実験してみましょう。

修正したStrict.ps1

$a = 3
1 * $a

修正したスクリプトを再度デバッグしてみます。

PS C:\Work> Set-PSDebug -Strict; ./Strict.ps1
3

未定義の変数がない場合には、エラーにならずに処理が行われることが確認できます。

未定義の変数を見つけたい場合には是非 -Strict パラメータを使用してみてください。

Thursday, July 17, 2008 10:15:25 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Sunday, July 13, 2008

PowerShellには、スクリプトのデバッグを行うために、Set-PSDebugというコマンドレットがあります。

書式は大きく2つに分けることができ

Set-PSDebug [-Trace 数値] [-Step] [-Strict]

Set-PSDebug -Off

です。[]がついている箇所は省略可能です。

-Traceパラメータ

-Traceパラメータに指定できる値には下記の3つがあります。

説明
0 スクリプトのトレースを無効にする
1 実行されるスクリプト行をトレースする
2 スクリプト行、変数の代入、関数呼び出し、およびスクリプトをトレースする

では、前回のスクリプトファイル(Debug.ps1)を使用して実験してみましょう。

$a = 2
$b = 3
$c = $a + $b
Write-Debug "Line4: $c"
$a = 4
$b = 5
$c = $a + $b
Write-Debug "Line8: $c"

-Traceに0を指定した場合

まずはSet-PSDebugコマンドレットによるスクリプトファイルのデバッグ方法ですが

Set-PSDebug -trace 0; ./Debug.ps1

のようにします。

-Traceパラメータに0を指定した場合の実行結果は下記の通りで、値0はトレースが無効となっているために1行だけ表示して終わっています。

デバッグ出力の行には、先頭に「デバッグ:」と表示さ異なる色で表されていることに注意してください。

PS C:\Work> Set-PSDebug -trace 0; ./Debug.ps1
デバッグ:    1+ Set-PSDebug -trace 0; ./Debug.ps1

-Traceに1を指定した場合は行トレースとなりますので、すべての行がデバッグ表示されます。

-Traceに1を指定した場合

PS C:\Work> Set-PSDebug -trace 1; ./Debug.ps1
デバッグ:    1+ Set-PSDebug -trace 1; ./Debug.ps1
デバッグ:    1+ $a = 2
デバッグ:    2+ $b = 3
デバッグ:    3+ $c = $a + $b
デバッグ:    4+ Write-Debug "Line4: $c"
デバッグ:    5+ $a = 4
デバッグ:    6+ $b = 5
デバッグ:    7+ $c = $a + $b
デバッグ:    8+ Write-Debug "Line8: $c"

-Traceに2を指定した場合

-Traceに2を指定した場合は変数の代入、関数呼び出しなど、各行で行われている詳細情報を見ることができます。

PS C:\Work> Set-PSDebug -trace 2; ./Debug.ps1
デバッグ:    1+ Set-PSDebug -trace 2; ./Debug.ps1
デバッグ:     ! CALL script 'Debug.ps1'
デバッグ:    1+ $a = 2
デバッグ:     ! SET $a = '2'.
デバッグ:    2+ $b = 3
デバッグ:     ! SET $b = '3'.
デバッグ:    3+ $c = $a + $b
デバッグ:     ! SET $c = '5'.
デバッグ:    4+ Write-Debug "Line4: $c"
デバッグ:    5+ $a = 4
デバッグ:     ! SET $a = '4'.
デバッグ:    6+ $b = 5
デバッグ:     ! SET $b = '5'.
デバッグ:    7+ $c = $a + $b
デバッグ:     ! SET $c = '9'.
デバッグ:    8+ Write-Debug "Line8: $c"

上記を見るとわかるように、一番最初に関数呼び出しを行っているので

! CALL script 'Debug.ps1'

と表示されています。

また、変数に値がセットされるときは

SET $a = '2'.

のように表示されていることがわかります。

 

上記をまとめると

-Traceは、どの行を通って来たかを確認するだけであれば"1"を、詳細情報(変数にセットされた情報、関数呼び出しなど)を得たい場合には"2"を指定すれば良いことがわかります。

うまく使い分けて、デバッグに活用してみてください。

Sunday, July 13, 2008 10:19:04 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Friday, July 11, 2008

スクリプトを開発し始めると、途中経過を確認しながら開発を進めたいと思うときはありませんか?

このようなときに使用するコマンドレットがWrite-Debugコマンドレットです。

このコマンドレットは、コンソールウィンドウにメッセージを表示するためのコマンドレットです。

コンソールにメッセージを表示するコマンドレットとしてはWrite-Hostコマンドレットがありますが、両者にはどのような違いがあるでしょう?

それは、

  • Write-Hostコマンドレットは、実行すれば必ず表示を行う。
  • Write-Debugコマンドレットは、$DebugPrefeenceによって表示制御が行われる

という違いがあります。

$DebugPreferenceは下記の3つの値を指定することが可能です。

意味
"Continue" Write-Debugに指定されたメッセージを表示
"SilentlyContinue" Write-Debugに指定されたメッセージを表示しない。既定値
"Stop" Write-Debugに指定されたメッセージを表示した後、動作を停止
"Inquire" Write-Debugに指定されたメッセージを表示後、続行するかを確認

 

それでは、下記スクリプト(Debug.ps1とします)を準備して、それぞれについて実験をしてみましょう。

$a = 2
$b = 3
$c = $a + $b
Write-Debug "Line4: $c"
$a = 4
$b = 5
$c = $a + $b
Write-Debug "Line8: $c"

Continue

まずは、下記のようにして $DebugPreferenceの設定を変更します。

PS C:\Work> $DebugPreference = "Continue"

つぎに、作成したスクリプトを実行してみましょう。

PS C:\Work> ./Debug.ps1
デバッグ: Line4: 5
デバッグ: Line8: 9

Write-Debugに指定したメッセージがきちんと表示されていますね。

SilentContinue

まずは、下記のようにして $DebugPreferenceの設定を変更します。

PS C:\Work> $DebugPreference = "SilentlyContinue"

スクリプトを実行します。

PS C:\Work> ./Debug.ps1
PS C:\Work>

Stop

まずは、下記のようにして $DebugPreferenceの設定を変更します。

PS C:\Work> $DebugPreference = "Stop"

スクリプトを実行します。

PS C:\Work> ./Debug.ps1
デバッグ: Line4: 5
Write-Debug : シェル変数 "DebugPreference" が Stop に設定されているため、コマンドの実行が停止されました
発生場所 C:\Work\Debug.ps1:4 文字:12
+ Write-Debug  <<<< "Line4: $c"

1つめのWrite-Debugで指定されたメッセージを表示した後、エラーメッセージを表示して処理を中断したことがわかります。

Inquire

まずは、下記のようにして $DebugPreferenceの設定を変更します。

PS C:\Work> $DebugPreference = "Inquire"

スクリプトを実行します。

PS C:\Work> ./Debug.ps1
デバッグ: Line4: 5

確認
この操作を続行しますか?
[Y] はい(Y)  [A] すべて続行(A)  [H] コマンドの中止(H)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):

1つめのWrite-Debugで指定されたメッセージを表示したあとに、スクリプトを実行するかを確認してくることがわかります。

ここで[Y]または[A]を押すと、スクリプトが続行されます。

確認
この操作を続行しますか?
[Y] はい(Y)  [A] すべて続行(A)  [H] コマンドの中止(H)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):
デバッグ: Line8: 9

確認
この操作を続行しますか?
[Y] はい(Y)  [A] すべて続行(A)  [H] コマンドの中止(H)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):

 

このように、Write-Debugコマンドレットを使用すれば、デバッグ時のみメッセージを表示すしそれ以外は表示しない、という制御を行うことができますので、是非活用してみてください。

Friday, July 11, 2008 10:49:53 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Tuesday, July 08, 2008

前回、trap文をスクリプトファイルの先頭に記述しましたが、これをスクリプトファイルの後半に記述したらどうなるかを実験しました。

break文での実験

1/$Null

Write-Host "スクリプト終了"

trap 
{
    "例外が発生しました"
    break
}

上記を実行した結果は下記の通りです。前回同様、例外をきちんとトラップして処理を中断することがわかります。

PS C:\Work> ./trap1.ps1
例外が発生しました
0 で除算しようとしました。
発生場所 C:\Work\trap1.ps1:1 文字:3
+ 1/$ <<<< Null

continue文での実験

1/$Null

Write-Host "スクリプト終了"

trap 
{
    "例外が発生しました"
    continue
}

上記を実行した結果は下記の通りです。こちらも前回同様、例外をきちんとトラップして処理を中断することがわかります。

PS C:\Work> C:\Work\trap2.ps1
例外が発生しました
スクリプト終了

このようにtrapを使用した例外処理はスクリプトファイルのどこにおいても有効です。

私個人としては、今回のようにスクリプトファイルの後半(例外が発生しそうなポイントの直後)へ配置するほうが好きです。

Tuesday, July 08, 2008 11:13:27 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Monday, July 07, 2008

PowerShellではTrap文を用いることで、例外処理を行うことができます。

trap文の構文は下記の通りです。

trap [<例外の種類>]
{
    trap文本体
}

「例外の種類」は省略することが可能です。

たとえば下記は、1/$Null を実行すると、ゼロ除算例外が発生するため、trapへ制御が移り "例外発生" の文字列を表示します。

PS C:\Work> trap
>> {"例外発生" }
>> 1/$Null
>>
例外発生
0 で除算しようとしました。
発生場所 行:3 文字:3
+ 1/$ <<<< Null

 

実際にスクリプトで使用する例を見ていきたいと思います。

まずはtrapなしの下記をメモ帳などで入力し、trap1.ps1 として保存してください。

1/$Null

Write-Host "スクリプト終了"

このスクリプトを実行すると下記のようにエラーメッセージが表示された後、「スクリプト終了」という文字が表示されます。

PS C:\Work> ./trap1.ps1
0 で除算しようとしました。
発生場所 C:\Work\trap1.ps1:1 文字:3
+ 1/$ <<<< Null
スクリプト終了

breakで処理を中断する

次に、例外を捕らえて処理を中断してみましょう。中断するにはbreakを使用します。(trap2.ps1)

trap 
{
    "例外が発生しました"
    break
}

1/$Null

Write-Host "スクリプト終了"

このスクリプトを実行すると下記のように、例外が発生するとTrapを実行するので「例外が発生しました」のメッセージとエラーメッセージを表示し、処理を中断します。

この結果、「スクリプト終了」の文字列は表示されません。

PS C:\Work> ./trap2.ps1
例外が発生しました
0 で除算しようとしました。
発生場所 C:\Work\trap.ps1:7 文字:3
+ 1/$ <<<< Null

continueを使用する

次にtrapの中でcontinueを使用するとどうなるかみてみましょう。(trap3.ps1)

trap 
{
    "例外が発生しました"
    continue
}

1/$Null

Write-Host "スクリプト終了"

実行結果は下記の通りで、例外が発生すると「例外が発生しました」のメッセージが表示され、最後の「スクリプト終了」が表示されます。

PS C:\Work> ./trap3.ps1
例外が発生しました
スクリプト終了

このようにcontinueを使用した場合はエラーメッセージは表示されず、trapを実行後、例外が発生した次の行から処理が再開されます。

Monday, July 07, 2008 11:12:51 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Saturday, July 05, 2008

以前 2>&1 演算子 を使用することでエラーの内容を変数に代入できることを説明しました。(エラーをファイルにリダイレクトする(PowerShell 入門)

この演算子を使用する以外に、各コマンドレットで共通に使用できる-ErrorVariableパラメータというものがあります。

このパラメータは下記のようにして使用します。

PS C:\Work> Get-ChildItem HIRO1.txt -ErrorVariable err1
Get-ChildItem : パス 'C:\Work\HIRO1.txt' が存在しないため検出できません。
発生場所 行:1 文字:14
+ Get-ChildItem  <<<< HIRO1.txt -ErrorVariable err1

-ErrorVariableパラメータの後ろには、エラー内容を代入するための変数を指定します。

このとき指定する変数に$を付加してはいけないことに注意してください。

以前紹介した$Error変数は、PowerShellの操作時に発生したすべてのエラーが格納されますが、この-ErrorVariableパラメータを使用した場合は、そのとき発生したエラーの内容だけを特定の変数に代入できるという違いがあります。

Saturday, July 05, 2008 10:47:20 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Thursday, July 03, 2008
Wednesday, July 02, 2008

PowerShellでは、エラーが発生した場合の振る舞いを変更することが可能です。

振る舞いの変更方法には2種類あり、コマンドレットごと、またはすべてのエラーに対して設定できます。

コマンドレットごとに指定する場合には -ErrorActionパラメータを、すべてのエラーに対して共通の振る舞いを行うようにするには$ErrorActionPreference変数を設定します。

設定可能な値は3つありますので、それぞれについてみてみましょう。

continue

デフォルトではContinueになっています。エラーが発生した場合はメッセージが出力されます。

エラーの内容は$Error変数に追加され、$?変数はfalseとなります。スクリプトは次の行から実行されます。

silentlycontinue

エラーが発生してもメッセージは出力されません。

エラーの内容は$Error変数に追加され、$?変数はfalseとなります。スクリプトは次の行から実行されます。

stop

エラーが発生してもメッセージは出力されません。

エラーの内容は$Error変数に追加され、$?変数はfalseとなります。スクリプトは停止します。

 

-ErrorActionパラメータの使用方法

-ErrorActionパラメータに"continue"を設定した場合はエラーメッセージが出力されます。

PS C:\Work> Get-ChildItem HIRO -ErrorAction "continue"
Get-ChildItem : パス 'C:\Work\HIRO' が存在しないため検出できません。
発生場所 行:1 文字:14
+ Get-ChildItem  <<<< HIRO -ErrorAction "continue"

-ErrorActionパラメータに"silentlycontinue"を設定した場合はエラーメッセージが出力されません。

PS C:\Work> Get-ChildItem HIRO -ErrorAction "silentlycontinue"
PS C:\Work>

$ErrorActionPreference変数の使用方法

$ErrorActionPreference変数へcontinueを設定する場合は下記のようにします。

PS C:\Work> $ErrorActionPreference = "continue"

$ErrorActionPreference変数へsilentlycontinueを設定する場合は下記のようにします。

PS C:\Work> $ErrorActionPreference = "silentlycontinue"

この状態で、さきほどエラーが発生したコマンドを実行してみましょう。

PS C:\Work> Get-ChildItem HIRO
PS C:\Work>

今度はエラーメッセージは表示されません。

$ErrorActionPreference変数にsilentlycontinueを設定した効果が現れていることがわかります。

このように、-ErrorActionパラメータや$ErrorActionPreference変数は、エラー発生時の振る舞いを変更することが可能ですので、ぜひ仕様用途に応じて使い分けてみてください。

Wednesday, July 02, 2008 10:54:23 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Tuesday, July 01, 2008

$?変数というものがあります。

これは処理全体が成功した場合にはtrue、それ以外の場合はfalseとなります。

この変数を使用すると、エラーメッセージが表示されなくてもエラーが発生したかどうかを確認できるため、スクリプトの中で使用すると効果を発揮します。

まずは、この変数の動きを見てみましょう。

なにもエラーが発生していない状態で$?変数の中身を確認すると

PS C:\Work> $?
True

上記のようにTrueを返します。

しかし下記のようにエラーを発生させると

PS C:\Work> dir HIRO2.txt
Get-ChildItem : パス 'C:\Work\HIRO2.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO2.txt
PS C:\Work> $?
False

上記のようにfalseを返します。

ではスクリプトの中でどう使用すればよいか?

1つ例を挙げたいと思います。

下記はdirコマンドを使用していますが、リダイレクトを使用してエラーを変数に代入するようにしています。

このためエラーはコンソールへは表示されません。

エラーメッセージが表示されないので、見た目にはエラーが発生したかどうかを確認できませんが、$?変数の中身を確認すれば、dirコマンドの実行結果が成功したか失敗したかを確認できるので、独自のエラー処理を記述することが可能です。

PS C:\Work>  $ret1 = dir HIRO 2>&1
PS C:\Work> if ( ! $? ) { "エラーが発生しました" }
エラーが発生しました

このように$Error変数や$?変数を使用すれば、エラーに対する処理を記述しやすくなりますので是非活用してみてください。

Tuesday, July 01, 2008 10:06:08 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [1]  |  Trackback
Monday, June 30, 2008

前回はErrorRecordオブジェクトについて説明しました。

PowerShellではエラーが発生するごとに、そのエラー内容(ErrorRecord)を保存している変数があります。

この変数は、"Error"という名前がついた自動変数でSystem.ArrayList型となっています。

また、この変数に格納できる最大要素数は自動変数 MaximumErrorCount で決められておりデフォルト値は256となっています。

では、実際にエラーを発生させてError変数がどのように変わっていくのかをみてみたいと思います。(PowerShellを立ち上げていた方は、念のため起動し直してから始めてください)

まずは、現在のError変数の内容を確認しておきましょう。

PS C:\Work> $Error
PS C:\Work>

この時点ではエラーは発生していないので、何も格納されていません。

 

次に前回同様、下記のように入力してエラーを発生させます。

PS C:\Work> dir HIRO.txt
Get-ChildItem : パス 'C:\Work\HIRO.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO.txt

エラーを発生させた後のError変数はどうなったでしょうか?

PS C:\Work> $Error
Get-ChildItem : パス 'C:\Work\HIRO.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO.txt

上記のように、先ほど発生した内容が格納されていることがわかります。

要素数はどうなったでしょうか?

PS C:\Work> $Error.Count
1

エラーは1つしか発生していないので、当然"1"となります。

では、このError変数が本当にErrorRecordオブジェクトを格納しているのかGetType()メソッドで確かめてみましょう。

(ここでは要素番号0の型をGetType()メソッドで確認しています。)

PS C:\Work> $Error[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ErrorRecord                              System.Object

次に、もう1つエラーを発生させてみましょう。

PS C:\Work> mkdir HIRO*2.txt
New-Item : パスに無効な文字が含まれています。
発生場所 行:1 文字:34
+ param([string[]]$paths); New-Item  <<<< -type directory -path $paths

この状態でError変数の内容を確認してみると

PS C:\Work> $Error
New-Item : パスに無効な文字が含まれています。
発生場所 行:1 文字:34
+ param([string[]]$paths); New-Item  <<<< -type directory -path $paths
Get-ChildItem : パス 'C:\Work\HIRO.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO.txt

少し見づらいですが、2つのエラーが格納されていることがわかります。

Error[0]には何が格納されているか確認してみましょう。

PS C:\Work> $Error[0]
New-Item : パスに無効な文字が含まれています。
発生場所 行:1 文字:34
+ param([string[]]$paths); New-Item  <<<< -type directory -path $paths

ご覧の通りインデックス0には、常に最後に発生したエラーが格納されます。

このように、Error変数はエラーの履歴をMaximumErrorCount変数で定められた数だけ保持しますので、過去にさかのぼってエラーを追跡することが可能です。

Monday, June 30, 2008 10:12:25 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Sunday, June 29, 2008

前回「エラーをファイルにリダイレクトする(PowerShell 入門) 」で2>&1演算子を使用してエラーの内容を変数に保存しました。

このようにしてエラーを格納すると、その変数はErrorRecordオブジェクトとなります。

ではErrorRecordオブジェクトにはどのようなメンバーが備わっているかを確認したいと思います。

まずは、前回同様下記のようにしてErrorRecord型の変数を作成しましょう。

$err = mkdir HIRO*2.txt 2>&1

次に、Get-Memberコマンドレットでどのようなメンバーがあるかを確認します。

PS C:\Work> $err | Get-Member


   TypeName: System.Management.Automation.ErrorRecord

Name                      MemberType   Definition
----                      ----------   ----------
Equals                    Method       System.Boolean Equals(Object obj)
GetHashCode               Method       System.Int32 GetHashCode()
GetObjectData             Method       System.Void GetObjectData(SerializationInfo info, StreamingContext context)
GetType                   Method       System.Type GetType()
get_CategoryInfo          Method       System.Management.Automation.ErrorCategoryInfo get_CategoryInfo()
get_ErrorDetails          Method       System.Management.Automation.ErrorDetails get_ErrorDetails()
get_Exception             Method       System.Exception get_Exception()
get_FullyQualifiedErrorId Method       System.String get_FullyQualifiedErrorId()
get_InvocationInfo        Method       System.Management.Automation.InvocationInfo get_InvocationInfo()
get_TargetObject          Method       System.Object get_TargetObject()
set_ErrorDetails          Method       System.Void set_ErrorDetails(ErrorDetails value)
ToString                  Method       System.String ToString()
writeErrorStream          NoteProperty System.Boolean writeErrorStream=True
CategoryInfo              Property     System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}
ErrorDetails              Property     System.Management.Automation.ErrorDetails ErrorDetails {get;set;}
Exception                 Property     System.Exception Exception {get;}
FullyQualifiedErrorId     Property     System.String FullyQualifiedErrorId {get;}
InvocationInfo            Property     System.Management.Automation.InvocationInfo InvocationInfo {get;}
TargetObject              Property     System.Object TargetObject {get;}

これらの中から、主要なメンバーについて説明したいと思います。

CategoryInfoプロパティ

このプロパティは文字通りエラーの種類についての情報が格納されています。

PS C:\Work> $err.CategoryInfo

Category   : InvalidArgument
Activity   : New-Item
Reason     : ArgumentException
TargetName : C:\Work\HIRO*2.txt
TargetType : String

CategoryでInvalidArgumentとなっていることがわかります。これはdirコマンドに対して、無効な文字が含まれたパスを指定しているためです。

さらにReasonをみるとこのArgumentExceptionという例外が発生したことがわかります。

Exceptionプロパティ

Exceptionプロパティ自体も様々なメンバーを持っていますが、規定のプロパティはMessageとなっており、エラーの内容を確認することができます。

PS C:\Work> $err.Exception
パスに無効な文字が含まれています。

InvocationInfoプロパティ

このプロパティはエラーの発生場所に関する情報が格納されます。

PS C:\Work> $err.InvocationInfo


MyCommand        : New-Item
ScriptLineNumber : 1
OffsetInLine     : -2147483648
ScriptName       :
Line             : param([string[]]$paths); New-Item -type directory -path $paths
PositionMessage  :
                   発生場所 行:1 文字:34
                   + param([string[]]$paths); New-Item  <<<< -type directory -path $paths
InvocationName   : New-Item
PipelineLength   : 1
PipelinePosition : 1

TargetObjectプロパティ

このプロパティにはエラー発生時の操作対象オブジェクトが格納されます。ただし、すべてのエラーがこのフィールドを設定する訳ではないため、Nullの場合もあります。

PS C:\Work> $err.TargetObject C:\Work\HIRO*2.txt

 

このようにErrorRecordオブジェクトにはエラーの原因を解明する上で役立つ情報が格納されています。

ぜひ、ErrorRecordオブジェクトの利用方法を理解し、スクリプトのデバッグに活用してください。

Sunday, June 29, 2008 10:56:02 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Saturday, June 28, 2008

今回から、PowerShellにおける、エラー/例外処理/デバッグ手法について説明していきたいと思います。

今回はエラーをファイルにリダイレクトする方法についてです。

まずは、「リダイレクトとは何か?」ですが、処理を行った結果の出力先を別な場所へ変更することを意味します。

通常PowerShell上で、あるコマンドを実行してエラーが発生した場合は、コンソールウィンドウにエラーメッセージが表示されます。

ここでは、このエラーをファイルに出力することをリダイレクトすると呼んでいます。

PowerShellでエラーをファイルにリダイレクトする方法にはリダイレクト演算子を使用し、3種類あります。1つずつみていきましょう。

2> 演算子

この演算子はエラーの内容をファイルに出力します。出力先のファイいるがすでに存在する場合には上書きをします。

下記は、dirコマンドで存在しないファイルを指定した場合の例です。

PS C:\Work> dir HIRO.txt
Get-ChildItem : パス 'C:\Work\HIRO.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO.txt

では、このエラーをファイルにリダイレクトしてみましょう。

PS C:\Work> dir HIRO.txt 2>err.txt

上記のように、2>演算子の後ろに、エラーメッセージを書き込むファイル名を記述します。

ファイル名だけを記述した場合は、カレントディレクトリに出力します。

では Get-Contentコマンドレットを使用して実際にファイルの中身をみてみましょう。

PS C:\Work> Get-Content err.txt
Get-ChildItem : パス 'C:\Work\HIRO.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO.txt 2>err.txt

エラーの内容がerr.txtに書き出されていることがおわかりいただけたかと思います。

2>> 演算子

先ほどの演算子と非常に良く似ていますが>が1つ多いですね。

この演算子は、エラーの出力先のファイルがすでに存在する場合は、追記書き込みを行います。

実験してみましょう。

追記されていることを確認するために、出力先のファイルは先ほどと同じerr.txtとします。また異なるエラーメッセージが記述されるようmkdirコマンドに変更しました。

PS C:\Work> mkdir HIRO*2.txt 2>>err.txt

実行したら、先ほど同様Get-Contentコマンドレットで確認してみましょう。

PS C:\Work> Get-Content err.txt
Get-ChildItem : パス 'C:\Work\HIRO.txt' が存在しないため検出できません。
発生場所 行:1 文字:4
+ dir  <<<< HIRO.txt 2>err.txt
New-Item : パスに無効な文字が含まれています。
発生場所 行:1 文字:34
+ param([string[]]$paths); New-Item  <<<< -type directory -path $paths

エラーメッセージが追記されていることがわかりますね。

2>&1 演算子

最後に2>&1演算子ですが、この演算子を使用するとエラーの内容を変数に書き込むことができます。

使用方法は下記の通りです。

$err = mkdir HIRO*2.txt 2>&1

では、エラーの内容が変数に代入されているかを確認してみましょう。

PS C:\Work> $err
New-Item : パスに無効な文字が含まれています。
発生場所 行:1 文字:34
+ param([string[]]$paths); New-Item  <<<< -type directory -path $paths

通常、変数の内容を確認してもメッセージに色がつかないのですが、2>&1演算子を使用して代入された値は赤色で表示されていることがわかります。

これは、変数の型がErrorRecordであり、PowerShellがエラーと認識しているからです。

PS C:\Work> $err.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     ErrorRecord                              System.Object

次回は、このエラーを保持する変数について説明したいと思います。

Saturday, June 28, 2008 10:32:42 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [1]  |  Trackback
Tuesday, June 24, 2008

ご無沙汰しております。おととい中国から帰国しました。

ということで、再開していきたいと思います。

Windows Formアプリケーションを作成する その1(PowerShell Tips)

Windows Formアプリケーションを作成する その2(PowerShell Tips)

の続きです。

前回までは、最初に空の関数を作成して、その中に

・受け取ったパラメータの確認
・パスの存在確認
・XMLファイルの読み込み処理

を追加しました。

今回は、データグリッドビューをフォームに追加してデータを表示する方法について説明します。

1.データグリッドビューの作成

まずデータグリッドビューですが

$dgvXML = New-Object System.Windows.Forms.DataGridView

のようにNew-Objectコマンドレットを使用してインスタンスを作成します。

2.DockStyleの設定

次に、DockStyleをFillに設定します。

$dgvXML.Dock = [System.Windows.Forms.DockStyle]::Fill

以前にも説明したのですが、列挙体を表す値を使用する場合は[]の中には列挙体名を::の右側へは列挙体値を指定します。

3.データをデータグリッドビューに表示する

次にデータの表示ですが、あらかじめ作成しておいたデータセットをDataSourceプロパティに指定します。

このとき、データはDataSetのTable[0]に入っていますので、それを指定します。

$dgvXML.DataSource = $dtSet.Tables[0]

4.データグリッドビューをフォームに追加する

最後に、データグリッドビューをフォームに追加します。

フォームに追加するにはControls.Addを使用します。

$form.Controls.Add($dgvXML)

次回はフォームを閉じるボタンの追加について説明します。

Tuesday, June 24, 2008 10:16:33 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Monday, June 09, 2008

スクリプトブロック(PowerShell 入門) の続きです。

スクリプトブロックではparamによる引数の受け取りが可能です。

PS > $script = { param($a); $a * 3 }

上記は、paramで引数を1つ受け取ります。

このスクリプトブロックは、引数を3倍します。

実行方法は、前回説明したように&演算子を使用して変数名を指定し、その後に引数として渡す値を記述します。

PS > & $script 5
15

もう1つ、$argsによる値の受け取りも可能です。

先ほどのスクリプトブロックを$argsで書き換えてみたのが下記です。

PS > $script = { $args[0] * 3 }

実行方法は、paramのときと同じで

PS > & $script 5
15

とします。

Monday, June 09, 2008 10:06:10 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Sunday, June 08, 2008

PowerShellでスクリプトブロックを作成して実行する方法について紹介したいと思います。

スクリプトブロックとは?

まず、スクリプトブロックとは何か? ですが、ほかの言語で言うと匿名関数やラムダ式に相当するものです。

名前のない関数ともいえるでしょうか。

PowerShellにおいては中括弧 {} で囲まれたスクリプトコードに過ぎません。

たとえば

PS > 1..10 | foreach { Write-Host $_ }

と記述したときの、{ Write-Host $_ } はスクリプトブロックです。

スクリプトブロックを変数に代入する

スクリプトブロックは、変数に代入することもできます。

たとえば

PS > $s = { $a * 3 }

上記は、変数$aを3倍するというスクリプトブロックを変数$sに代入したことになります。

スクリプトブロックを実行する

では、スクリプトブロックを実行するにはどうすれば良いでしょうか?

PS > $s

上記のように変数名を入力して[Enter]を押すだけでは実行数することができません。

スクリプトブロックを実行するには、下記のように &演算子を使用します。(&とスクリプトブロックが代入された変数との間にはスペースが必要です)

PS > $a =2
PS > & $s
6

次回、スクリプトブロックでのパラメータの受け取り方法について説明したいと思います。

Sunday, June 08, 2008 9:49:04 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Monday, June 02, 2008

PowerShellには、関数とは別にパイプを通して受け取ったオブジェクトを処理するフィルタと呼ばれるものがあります。

書式は関数とほぼ一緒なのですが、functionの代わりにfilterキーワードを使用します。

filter <フィルタ名> (引数) {実行するスクリプト}

関数と同様、結果をreturnで返すことも可能です

 

唐突ですが、ここで問題です。

「現在稼働しているサービス一覧を取得してください」と言われたら皆さんならどうしますか?

答えは下記の通りです(一例です)

PS > Get-Service | Where { $_.Status -eq "Running" }

Status   Name               DisplayName
------   ----               -----------
Running  AeLookupSvc        Application Experience
Running  AppHostSvc         Application Host Helper Service
Running  AudioEndpointBu... Windows Audio Endpoint Builder
Running  Audiosrv           Windows Audio
Running  BFE                Base Filtering Engine
:
:長いので省略
:

Get-ServiceコマンドレットとWhereを使用してステータスが"Running"のものをフィルタしています。

この $_.Status -eq "Running" はフィルタ化することが可能です。

では、フィルタ名を StsRunning としてフィルタを作成してみましょう。

PS > filter StsRunning {
>> if ( $_.Status -eq "Running" )
>> {
>>   return $_
>> }
>> }
>>

こうして作成したフィルタの使用方法は簡単で、パイプの後ろに記述するだけです。

PS > Get-Service | StsRunning

Status   Name               DisplayName
------   ----               -----------
Running  AeLookupSvc        Application Experience
Running  AppHostSvc         Application Host Helper Service
Running  AudioEndpointBu... Windows Audio Endpoint Builder
Running  Audiosrv           Windows Audio
Running  BFE                Base Filtering Engine

このように、よく使用するフィルタ条件は、専用のフィルタを作成しておくと便利です。

フィルタはコンソール上で作成すると、PowerShell終了時に消滅してしまうので、プロファイルに登録することをおすすめします。(「関数をプロファイルに追加する」をご参考ください)

最後に、関数とフィルタの違いについてですが

関数は、パイプで受け取ったオブジェクトを一度に処理する

フィルタは、パイプで受け取った個々のオブジェクトごとに実行する

という違いがあります。

Monday, June 02, 2008 11:02:33 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Sunday, June 01, 2008

PowerShellでは、パイプラインで受け取った値を処理する方法にbegin/process/endというものが存在します。

まずは、下記スクリプトを入力してください。

PS > function f
>> {
>> begin { $local:cnt = 1 }
>> process { ("$local:cnt:$_"); $local:cnt += 1 }
>> end { $local:cnt }
>> }
>>

このスクリプトには、begin/process/end がありますが、パイプラインから値を受け取ると

begin {最初の1回だけ実行}
process {パイプラインで受け取った値ごとに実行}
end {最後に1回だけ実行}

という動作をします。

つまり、入力していただいた関数f は

最初にローカル変数cntを1で初期化
パイプラインから受け取った値を($_に値が代入されている)表示し、ローカル変数をインクリメント
最後にローカル変数の現在値を表示

ということを行います。

前回のパイプライン引数(PowerShell)入門では、パイプライン引数の値は$input自動変数に入ることを説明しました。

process節でパイプラインの値を取得する場合は $_ 自動変数となりますので、使用には注意してください。

説明が長くなりましたが、実際にこの関数にパイプラインから値を渡してみましょう。

PS > "apple","banana","orange" | f
1:apple
2:banana
3:orange
4

パイプラインからの値処理で前処理と後処理を行いたい場合は、是非使用してみてください。

Sunday, June 01, 2008 11:53:35 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Saturday, May 31, 2008

PwerShellでの関数は、パイプラインからの値を受け取ることが可能です。

パイプラインからの渡された値は $input という自動変数に格納されます。

まずは、コンソールで下記の通り入力し、pipe-func1 という関数をPowerShellに登録しましょう。

PS > function pipe-func1
>> {
>>     foreach ($a in $input)
>>     {
>>         Write-Host $a
>>     }
>> }
>>

この関数は、パイプラインで受け取った値をforeachで1つずつ取り出し表示するという単純な関数です。

以下のようにして、この関数を実験してみてください。

PS > 1..10 | pipe-func1
1
2
3
4
5
6
7
8
9
10

1..10 というのは、1から10までを表します。(範囲演算子(PowerShell 入門)を参照ください)

結果としてこの関数は$inputに1から10までの数値を受け取り、その値を表示します。

もう1つ実験してみましょう。

PS > "apple","banana","orange" | pipe-func1
apple
banana
orange

こちらは"apple","banana","orange"という3つの値が$inputに渡され、結果その値を表示します。

Saturday, May 31, 2008 10:15:35 PM (Tokyo Standard Time, UTC+09:00)  #    Comments [0]  |  Trackback
Friday, May 30, 2008