2011年12月3日土曜日

◆コマンドレットの難易度を調べる

Advent Calendarに初めて参加させていただきます。
このようなIT系のイベントとは無縁でしたので自分が書く立場になるとは夢にも思わず、何を書こうかだいぶ考えてしまいました。

難しいことは書けないし簡単すぎてもつまらない。
そこで、月並みですがここは普通にコマンドレットの解説でもと思い立ち、難しめのコマンドレットの使い方を調べてみようかと考えました。

しかし、そこでハタと考えたのですが難しいコマンドレットとはさて何だろう・・・。
個人的に難しい(というかよく分からない)コマンドレットはあるのですが、そこは人それぞれの事情で色々と違ってくるでしょうし。
やはり何か客観的な尺度が欲しい。

そこで、PowerShell Advent Calendar 2011 : ATNDのトップバッターfsugiyamaさんのPowerShell - コマンドレットやオブジェクトなどの調査に関する20問 - @fsugiyamaの技術日誌を見ていてふと考えたのが、ヘルプのサイズがデカイのが難しいコマンドではないかと。
単純に正比例とは行かないでしょうが、少なくともかなりの相関関係はありそう。

という訳で、まずはヘルプの行数をカウントするスクリプトを作ってみる。

001
002
003
004
005
006
007

Get-Help -Category cmdlet -Full | ?{$_.Category -eq "cmdlet"} | %{
 
$help = New-Object PsObject | select name,content,contentCount
  $help.name = $_.
name
 
$help.content = $_ | Out-String -Stream
  $help.contentCount = $help.content.
Count
 
$help
} | sort contentCount | select name,contentCount

こんな感じかなと思っだのだが結果は、

image

かなり残念な感じになる。(ちなみに、Get-Helpはcmdletをカテゴリーとして指定してもエイリアスも含まれてしまうようなのでもう一度フィルターしている)

そこで、三種の神器のひとつ「Get-Member」で調査してみる。

image

image

カテゴリーを指定したほうは「HelpInfoShort」という型が帰ってきているのがわかる。
Shortではいかにもダメそう。(出来なくもなさそうだがちょっと面倒な感じ)

という事でちょっとやり方を変えて、やはり「三種の神器」Get-Commandを使うこととした。

001
002
003
004
005
006
007
008
009
010
011

$script:i = 0
Get-Command -CommandType cmdlet | %
{
 
$help = New-Object PsObject | select name,content,contentCount
  $help.name = $_.
name
 
$help.content = Get-Help $_.name -full | Out-String -Stream
  $help.contentCount = $help.content.
Count
 
$help
} | 
sort contentCount -Descending | 
ft @{name="NO";expression={($script:i++)}} ,
  name,contentCount -auto

最初に「Get-Command」で「cmdlet」タイプのコマンドを取得してから「Get-Help」でヘルプファイルを取得。
取得したコマンド名とヘルプ内容と、その行数を保持するオブジェクトを作るために「New-Object」コマンドレットで「PsObject」を作成。
「PsObject」はこういう時に汎用的に使えるオブジェクト(だと自分は思っている)。
そのオブジェクトにプロパティを追加するために、ここでは「Select-Object」コマンドレットを使っている。
本来プロパティを追加するには「Add-Member」コマンドレットを使うのが正しいやり方だと思うのだが、存在しないプロパティを「Select-Object」するとそのプロパティを追加してくれるので簡単にプロパティが追加できる。
これはだいぶ前に偶然見つけた方法なのでまっとうな方法では無いかもしれないが「Add-Member」よりは簡単だ。
もっと手を抜くと「PsObject」も作らずに、

$help = 1 | select name,content,contentCount

なんて書いても大丈夫。
型は当然「Int32」になるが特に弊害は無いだろう。

(追記)
New-Objectを使う場合は、Propertyパラメータを使うとAdd-Memberしなくてもプロパティを指定できます(C#のオブジェクト初期化子みたいな感じ)。

あとはソートしてから、順位を振るために集計プロパティを使っている。
(このようなときはスクリプト変数を使うと良い)
集計プロパティとはSelectコマンドレットで単純にプロパティをそのまま表示するのではなく、編集した形で表示したいときなどに使用する。
単純に値を編集して表示したいだけなら、以下のような指定でもよい。
image

ただし、これだと項目名(タイトル)に式がそのまま使われてしまい見栄えがよろしくない。
そこで、もともとの式を「expression」という名前でブロック指定、タイトルを「name」として指定したものを連想配列形式で使用したものが集計プロパティということになる。
ちなみに、日本語では「集計プロパティ」と訳されているがオリジナルは「calculated property」っぽい。「集計」は「遠からずとも当たらず」といった感じがするがいかがなものだろう・・・。

さて、実際にヘルプを集計した結果は以下のとおり。
image

栄えある第1位は「Invoke-Command」、続いて「Get-WSManInstance」「Get-WmiObject」となった。
なお、少ない方は以下のとおり。
image

実に20倍近く違っている。

さて、それでは第1位の「Invoke-Command」のヘルプを開いてみる。
さすがに900行のボリュームは一息に眺めるにはちょっと厳しい。
最初はサンプルを全部試してみようと思っていたのだが、サンプルだけで15個あり、しかも「Invoke-Command」の場合はリモートのサーバーにコマンドを投げるケースの説明が半分以上なため、複数台環境を作らないと試すのも難しい。
実は、かつてリモートの環境をつくろうとして挫折しそのままになっている(^^;
どうも、ドメイン環境では単純に繋がらないっぽい。(グループポリシーを変更しないといけないのかもしれないが会社のグループポリシーは簡単には弄れない)
もしかすると原因は他のところにあるのかもしれないが・・・・。

ということで「Invoke-Command」自体の機能調査はまたの機会にすることとし、ヘルプファイルの見方だけを考察し終わりにしたい。

そもそも「Invoke」の意味はと言うと、「呼び出す、実行する」とかいう意味なので単純に解釈すればコマンドを実行するという意味なのでしょう。
通常のコマンドであれば当然そのままでも実行可能なので、やはり使い所としては他のコンピュータで実行させるというところなのかもしれません。
個人的には、Invokeという単語との初めての出会いはGUIスレッドに処理を投げるというものだったので「依頼する」的な意味に解釈しています。

まず構文を見ると7種類もの構文があります。
構文というのは、パラメーターの組み合わせのセットという感じなので、C#とかでいうオーバーロードと捉えておけば良いのかと思っています。

構文の次に、コマンドの説明があり、各パラメータの説明と続きますが、パラメータの説明は必ずこんな記述になっていますのでこの意味を理解しておくことが重要になります。
image

「必須」に対するtrue,falseは説明不要と思いますが、「位置」というのはそのパラメータを位置パラメータで指定できるのか名前パラメータで指定するのかということです。(名前での指定は常にOKとおもいますが)
位置パラメータとして指定できるときは、その順番が「1」とか「2」とかの数字で記載されます。位置パラメータが使えないときは「named」の記載になります。
「規定値」がある場合はここに記載されますがあまり使用しているケースは無いように思います。
若干面倒なのが「パイプライン入力を許可する」。
許可する場合は(ByValue)と(ByPropertyName)の2種類があります。
(ByValue)の場合はパイプライン入力オブジェクトの値をこのパラメータにマッピング、(ByPropertyName)の場合はパイプライン入力オブジェクトにこのパラメータと同名のプロパティがあればそれにマッピングとなります。

この時例えば(ByValue)と(ByPropertyName)の両方が入力オブジェクトに一致する場合どちらが優先されるのだろうなどと思ってしまいますが、どうやら(ByValue)が優先されるようです。

若干脱線気味ですが、このようなことを調べるのには「Trace-Command」コマンドレットが使えます。
例えば、「Get-Process」コマンドレットは「InputObject」パラメータに(ByValue)、
image
「Name」パラメータに(ByPropertyName)となっています。
image

そこでプロセスオブジェクト(Nameプロパティを持つ)をパイプライン入力に与えるとどちらが優先されるのかを調べたのが以下のコマンドです。

image

「InputObject」パラメータにマップされているのが判ります。

こんな感じでだいたいパラメータの使い方が判るので、あとは「入出力」「メモ」などの注意事項を眺めながら「サンプル」を試していくといった感じでしょうか。
image

さしあたって最小構成でサンプルを実行するとこんな感じになります。
image

ただし、これだけであれば「呼び出し演算子」を使ったほうが楽なので、やはり各種パラメータと組み合わせて使うコマンドと言う事になりそうです。
image

今回、難しいコマンドレットという事で順位を付けてみましたが、やはり上位に来ているものの中には普段ちょっと敬遠しているものも多いと感じました。
機会があれば、今後少しずつ勉強していきたいと思います。

若干取り留めのない内容となりましたが、最後までお付き合いいただきありがとうございました。

0 件のコメント:

コメントを投稿