きっかけはカナダのMVP、Thomas Raynerさんのブログエントリから。
thomasrayner.ca
PowerShellにおいて予め*.format.ps1xml
によって表示書式が定められてないオブジェクトは、表示されるプロパティの数が4個以下の場合はTableフォーマットを、5個以上になる場合はListフォーマットを取ります。*1
例としてプロパティの数が異なる簡単なPSCustomObject
を挙げます。
[PSCustomObject]@{Prop1=1;Prop2=2}
[PSCustomObject]@{Prop1=1;Prop2=2;Prop3=3}
[PSCustomObject]@{Prop1=1;Prop2=2;Prop3=3;Prop4=4}
[PSCustomObject]@{Prop1=1;Prop2=2;Prop3=3;Prop4=4;Prop5=5}
このコードを実行すると下図の様にプロパティの数が5になった時点でListフォーマットに変化していることが分かります。
このプロパティ数の根拠は何なのか?
このはなしに関しては、どのドキュメントだったかは失念したのですが、以前にも聞いたことがありました。
当時は「そんなものなのかなぁ...」となんとなく流していたのですが今はPowerShellはオープンソースとなっています。
せっかくなのでソースからこのプロパティ数の根拠を調べてみました。
調査したソースのバージョンは現時点の最新であるPowerShell 6.0 Beta7ですが、PowerShellの根幹にかかわる部分ですので過去のバージョンでもさほど変わらないと思います。
調査結果
結果としては、Microsoft.PowerShell.Commands.Internal.Format.FormatViewManagerクラスのこのあたりが怪しそうです。
それっぽいコメントが記載されています。
_viewGenerator = SelectViewGeneratorFromProperties(shape, so, errorContext, expressionFactory, db, null);
で、このSelectViewGeneratorFromProperties
メソッドは内部で、Microsoft.PowerShell.Commands.Internal.Format.DisplayDataQueryクラスのGetShapeFromPropertyCountメソッドを呼んでいます。
名前からして正解に近そうです。
private static ViewGenerator SelectViewGeneratorFromProperties(FormatShape shape, PSObject so,
TerminatingErrorContext errorContext,
MshExpressionFactory expressionFactory,
TypeInfoDataBase db,
FormattingCommandLineParameters parameters)
{
shape = DisplayDataQuery.GetShapeFromPropertyCount(db, expressionList.Count);
このGetShapeFromPropertyCount
メソッドの定義は以下の様になり、
internal static FormatShape GetShapeFromPropertyCount(TypeInfoDataBase db, int propertyCount)
{
if (propertyCount <= db.defaultSettingsSection.shapeSelectionDirectives.PropertyCountForTable)
return FormatShape.Table;
return FormatShape.List;
}
表示対象となるプロパティの数が
db.defaultSettingsSection.shapeSelectionDirectives.PropertyCountForTable
以下であればTableフォーマット(FormatShape.Table
)、そうでなければListフォーマット(FormatShape.List
)を選ぶ様になっています。
いい感じです。
PropertyCountForTable
の内容を調べれば正解にたどり着けそうです。
これはMicrosoft.PowerShell.Commands.Internal.Format.ShapeSelectionDirectivesクラスに定義されており、以下の様になっています。
internal int PropertyCountForTable
{
set
{
if (!_propertyCountForTable.HasValue)
{
_propertyCountForTable = value;
}
}
get
{
if (_propertyCountForTable.HasValue)
return _propertyCountForTable.Value;
return 4;
}
}
private int? _propertyCountForTable;
_propertyCountForTable
に値が定義されてればその値を使い、値が定義されていない場合は4
を返す様になっています。
詳細は後述しますが通常_propertyCountForTable
には値がセットされておらず4
を使う様になっています。
プロパティ数の4
の根拠はここの様です。
PowerShell既定のフォーマット設定に介入する方法
ここから本エントリの本題に入ります。
先ほど判明したPropertyCountForTable
プロパティおよび_propertyCountForTable
についてですが、さらに調査をしたところ、PowerShellのオブジェクトの書式定義を行う.format.ps1xml
ファイルから設定可能であることが分かりました。
既定の.format.ps1xml
ファイルはPSHOME
ディレクトリに複数用意されており、また、PowerShell 5.1からはこのファイルと対になる内部クラスを使う様に置き換えられていますが、これらのファイル・クラスを調べてもPropertyCountForTable
プロパティを設定している個所はありませんでした。
このため先に述べた様に既定値の4
が使われています。
.format.ps1xml
ファイルはユーザーが独自に定義することが可能です。
独自に作成した.format.ps1xml
ファイルからPropertyCountForTable
プロパティに介入できないか試してみたところ、このプロパティの値を変えることができました。
手順は次の通りです。
最初に、以下の様なXMLファイルを作成して任意の名前で保存します。
PropertyCountForTable
タグの値がPropertyCountForTable
プロパティに指定する値となります。(今回は2にしています)
xml version="1.0" encoding="utf-8"
<Configuration>
<DefaultSettings>
<PropertyCountForTable>2</PropertyCountForTable>
</DefaultSettings>
</Configuration>
このXMLファイルをUpdate-FormatData
コマンドレットで読み込んでやればPropertyCountForTable
プロパティの値を更新できます。
Update-FormatData -AppendPath .\sample.format.ps1xml
実行例は以下の様になりフォーマットが変化するプロパティの数が2に変わっていることがわかります。
ちなみに、本エントリはPowerShell 5.1の環境で動作確認していますが、PowerShell 2.0の環境でも介入可能でした。
最後に
とりあえずこんな感じです。
PowerShellがオープンソース化してくれたおかげで以前はわからなかった挙動に対する理解を深めることができました。
すばらしいですね。
【2017/10/06追記】
補足を書きました。
blog.shibata.tech