はじめに
本記事は PowerShell Advent Calendar 2018 の 6 日目です。
PowerShell Advent Calendar 2018 は寄稿して頂ける方を絶賛募集中です。よろしくお願いいたします。
qiita.com
動機
PowerSheller であれば、Get-Module -ListAvailable って、数えきれないほど叩いてきたと思います。
このコマンドの出力は、こんな感じになりますね。
PS> Get-Module -ListAvailable ディレクトリ: C:\Users\aetos\Documents\WindowsPowerShell\Modules ModuleType Version Name ExportedCommands ---------- ------- ---- ---------------- Script 0.7.0 Az.Aks {Get-AzAks, New-AzAks, Remove-AzAks, Import-AzAksCredential...} Script 0.7.0 Az.AnalysisServices {Resume-AzAnalysisServicesServer, Suspend-AzAnalysisServicesServer, Get-AzAnalysisServicesServer, Rem... Script 0.7.0 Az.ApiManagement {Add-AzApiManagementRegion, Get-AzApiManagementSsoToken, New-AzApiManagementHostnameConfiguration, Ne... ... ディレクトリ: C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules ModuleType Version Name ExportedCommands ---------- ------- ---- ---------------- Manifest 1.0.1.0 ActiveDirectory {Add-ADCentralAccessPolicyMember, Add-ADComputerServiceAccount, Add-ADDomainControllerPasswordReplica... Manifest 1.0.0.0 AppBackgroundTask {Disable-AppBackgroundTaskDiagnosticLog, Enable-AppBackgroundTaskDiagnosticLog, Set-AppBackgroundTask... Manifest 2.0.0.0 AppLocker {Get-AppLockerFileInformation, Get-AppLockerPolicy, New-AppLockerPolicy, Set-AppLockerPolicy...} ...
一方、PowerShellGet で導入された類似コマンドである Get-InstalledModule の出力は、こんな感じです。
PS> Get-InstalledModule Version Name Repository Description ------- ---- ---------- ----------- 5.1.2 Azure PSGallery Microsoft Azure PowerShell - Service Management 0.5.0 Azure.AnalysisServices PSGallery Microsoft Azure PowerShell - Analysis Services server management 4.2.1 Azure.Storage PSGallery Microsoft Azure PowerShell - Storage service cmdlets. Manages blobs, queues, tabl... ...
個人的には、この Get-InstalledModule の結果が、Get-Module のように、インストール ディレクトリでグループ化されないことが不満でした。
Get-Module は、別にいいんですよ。どこにインストールされていようが関係ないんです。インポートさえできれば。
でも、Get-InstalledModule は、インストール場所を気にすることが、一点だけあります。
それはモジュールの更新時。
ユーザー プロファイル ディレクトリ(C:\Users\aetos)以下にインストールされていれば、ユーザー権限で更新ができます。
グローバル インストール(C:\Program Files\WindowsPowerShell\Modules)だったら、管理者権限でないと更新ができません。
というわけで、(やや強引な動機ではありますが)Get-InstalledModule の結果が、インストール場所でグループ化されるようにしてみましょう。
ま、過去にこんなことがあったので、ちょっと気にしているという理由はあります。
tech.blog.aerie.jp
目標
実現したいイメージはこうです。
PS> Get-InstalledModule ディレクトリ: C:\Users\aetos\Documents\WindowsPowerShell\Modules Version Name Repository Description ------- ---- ---------- ----------- 5.1.2 Azure PSGallery Microsoft Azure PowerShell - Service Management 0.5.0 Azure.AnalysisServices PSGallery Microsoft Azure PowerShell - Analysis Services server management 4.2.1 Azure.Storage PSGallery Microsoft Azure PowerShell - Storage service cmdlets. Manages blobs, queues, tabl... ...
言葉で書くと
- 新しいコマンドは導入せず、Get-InstalledModule でできるようにする
- 出力結果をインストール場所でグループ化する
- 出力結果の互換性を保つ
の 3 つです。
「出力結果の互換性を保つ」というのは、出力されるオブジェクトのメンバーをいじらないということです。
もし Get-InstalledModule コマンドを内部で実行しているスクリプトがあっても、それが壊れないようにするためです。
完成品
こちらです。
gisteb9543af259e38346acde5f1440b8b74
この 2 つのファイルを同じディレクトリにおいて、PowerShell プロファイルで PowerShellGetUtility.ps1 を読み込むだけです。
説明
やっていることは単純です。
同名、同引数の Get-InstalledModule というコマンドを定義し、その中で本来の PowerShellGet\Get-InstalledModule コマンドを呼び出し、結果を InstalledLocation でソートして、独自の型名を付けて返しているだけですね。
グループ化は、Add-Member でつけた型名に対して、InstalledModule.format.ps1xml で設定しています。
アドホックにやるなら、Format-Table コマンドの -GroupBy オプションで可能です。
ただ、コマンド内で Format-Table したものを出力してしまうと、型の互換性が崩れてしまいます。
変えたいのは表示だけなので、format.ps1xml ファイルで行うのが適切です。
format.ps1xml でやるにせよ、Format-Table でやるにせよ、グループ化したい値でソートしておかないと正常に動きません。
もし、こうしたコマンド自体の追加処理が不要なのであれば、ps1xml だけでも可能です。
tech.blog.aerie.jp
モジュールの InstalledLocation プロパティは
C:\Users\aetos\Documents\WindowsPowerShell\Modules\Az\0.7.0
のようになっていますので、ここからモジュール名とバージョンを削るために Split-Path -Parent を 2 回呼んだものでグループ化しています。
追加したオプション -Raw は、何も手を加えない、オリジナルの Get-InstalledModule を実行するためのオプションです。
まぁ、
PS> PowerShellGet\Get-InstalledModule
って叩いても同じことなんですが、こっちのほうが楽なので。
出力オブジェクトには(型名を追加した以外は)手を加えていないので、今まで通り
PS> Get-InstalledModule | Update-Module
と打っても動きます。
SteppablePipeline でごにょごにょしているのは、パイプライン経由で呼ばれた場合に、
- このコマンドの begin
- PowerShellGet\Get-InstalledModule の begin
- このコマンドの process
- PowerShellGet\Get-InstalledModule の process
- このコマンドの end
- PowerShellGet\Get-InstalledModule の end
という順番で実行されるようにするためです。
まぁ今回の場合、(-Raw をつけなければ)Sort-Object しているので、主な処理は end に寄ってしまうのですが。
ヘルプについても、
.ForwardHelpTargetName PowerShellGet\Get-InstalledModule
と書くことで、
Get-Help Get-InstalledModule
と打った時に、オリジナルのヘルプが出るようにしてあります。
既存のコマンドと同名のコマンドを定義することで、既存のコマンドをオーバーライドしてしまうのが良いことなのかと思われるかもしれません。
が、これは PowerShell 的に OK なのです。
Out-Default コマンドの説明を見るとわかるのですが、これは、まさにそのようにオーバーライドされることを意図して用意されたコマンドです。
Out-Default コマンドが特別というよりは、一般的に OK なことであると解釈しています。
まとめ
短いながらも、いろいろなエッセンスが詰まったサンプルになっており、応用範囲はそれなりに広いと思われます。
機会があったら参考にしてみてください。