前回までで、PowerShellスクリプトがかなり仕上がってきた。今回は、PowerShellに用意されている機能を使ってさらにPowerShell風のスクリプトに仕上げていこう。今回取り組むのは、Get-Help対応だ。

前回仕上げたスクリプト「test-d_13.ps1」は次の通りだ。引数の処理として「Param()」を使うように変更し、パラメータ指定時に補完機能が効くようになったことなどを確認した。

#!/usr/bin/env pwsh

#====================================================================
# 引数処理
#====================================================================
Param(
    [string]$DirectoryPath,
    [switch]$Help
)

if ($Help -Or -Not $DirectoryPath) {
    $scriptname = Split-Path -Leaf $PSCommandPath
    $errmsg = "usage: $scriptname " `
        + "[-DirectoryPath] ディレクトリパス " `
        + "[-Help]"
    Write-Error $errmsg
    exit 1
}

#====================================================================
# ディレクトリパスの存在確認処理
#====================================================================
if (Test-Path -PathType Container -Path $DirectoryPath) {
    # 指定されたパスはディレクトリであり、存在している。
    $True
} else {
    # 指定されたパスはディレクトリではない。
    if (Test-Path -PathType Leaf -Path $DirectoryPath) {
        # 指定されたパスはファイル。
        $infomsg = "$DirectoryPath : " `
            + "ファイルです。"
    } else {
        # 指定されたパスは存在しない。
        $infomsg = "$DirectoryPath : " `
            + "そのようなディレクトリは存在しません。"
    }
    Write-Host $infomsg
    $False
}

このスクリプトでは-Helpパラメータでスクリプトの簡単な使い方が表示されるようにしたわけだが、この書き方だとGet-Helpコマンドレットは次のように本当に最小限の出力しか行わない。

「Get-Help」を使ったときの出力

PowerShellでは、Get-Helpコマンドレットを使うことで、ほかのコマンドレットやモジュール、関数などのマニュアル(ヘルプ)を表示させることができる仕組みになっている。これはスクリプトにおいても同様で、「Get-Help」で使用できるようにヘルプを書いておけば、Get-Helpコマンドレットでもっときちんとした出力が行われるようになる。今回は、この部分の書き換えを行っていく。

「Get-Help」に対応したヘルプの書き方

スクリプトの場合、スクリプトが始まった直後に規定のフォーマットのコメントを書くことが「Get-Help」に対応する方法になっている。フォーマットは非常にシンプルなroffのようなもので、例えば次のように書けばよい。

<#
.SYNOPSIS

Adds a file name extension to a supplied name.

.DESCRIPTION

Adds a file name extension to a supplied name.
Takes any strings for the file name or extension.

.PARAMETER Name
Specifies the file name.

.PARAMETER Extension
Specifies the extension. "Txt" is the default.

.INPUTS

None. You cannot pipe objects to Add-Extension.

.OUTPUTS

System.String. Add-Extension returns a string with the extension
or file name.

.EXAMPLE

PS> extension -name "File"
File.txt

.EXAMPLE

PS> extension -name "File" -extension "doc"
File.doc

.EXAMPLE

PS> extension "File" "doc"
File.doc

.LINK

http://www.fabrikam.com/extension.html

.LINK

Set-Item
#>

上記サンプルは、次のドキュメントに掲載されているものだ。


基本的に「Get-Help」に対応したヘルプの書き方は上記のドキュメントにまとまっているので、これを参考にして書けばよい。

「.(ピリオド)」から始まっている大文字で記述された行は「キーワード」と呼ばれ、ヘルプで言うところの「セクションタイトル」に相当する。キーワードに続く行がそのセクションの内容だ。今作成しているスクリプトについて「Get-Help」に対応したコメントを書くとすれば、次のような感じになるだろう。

<#
.SYNOPSIS
ディレクトリパスが存在するかどうかを判定。

.DESCRIPTION
指定されたパスがディレクトリパスであり、かつ、存在するかどうかを判定する。

.PARAMETER DirectoryPath
ディレクトリパスを指定。

.PARAMETER Help
ヘルプメッセージを表示。

.INPUTS
なし。パイプには対応していない。

.OUTPUTS
System.Boolean。ディレクトリパスが存在する場合にTrue、そうでない場合にFalse。

.EXAMPLE
PS> .\test-d .\Documents\
True

.EXAMPLE
PS> .\test-d .\Documents-non-exists\
False

.LINK
Test-Path
#>

キーワードには次のようなものが用意されている。

キーワード 内容
.SYNOPSIS スクリプトの簡単な説明。1回だけ使用可能
.DESCRIPTION スクリプトの詳しい説明。1回だけ使用可能
.PARAMETER パラメータ名 パラメータの説明
.EXAMPLE サンプルを記述
.INPUTS パイプラインで接続できるオブジェクトの.NET型
.OUTPUTS 出力されるオブジェクトの.NET型
.NOTES スクリプトの追加情報
.LINK 関連するトピックやURI
.FORWARDHELPTARGETNAME コマンド名 指定したコマンドのヘルプへリダイレクト
.FORWARDHELPCATEGORY カテゴリ Alias、Cmdlet、HelpFile、Function、Provider、General、FAQ、Glossary、ScriptCommand、ExternalScript、Filter、All
.REMOTEHELPRUNSPACE PSSession値 ヘルプトピックを含むセッションを指定
.EXTERNALHELP XMLヘルプファイル XMLベースヘルプファイル

表のキーワードは上のものほどよく使うもので、下にいくほどあまり使われないものになっている。「Get-Help」に対応したコメントはスクリプトの仕様書としても使えるものだ。スクリプトを書くときは最初にこのコメントを書き、その通りに動作するようにスクリプトを仕上げていくというのが書き方の一つにもなる。

「Get-Help」に対応させたスクリプト

では、「Get-Help」に対応させたスクリプトに書き換えてみよう。今回書き換えたスクリプト「test-d_14.ps1」は次の通りだ。

#!/usr/bin/env pwsh

<#
.SYNOPSIS
ディレクトリパスが存在するかどうかを判定。

.DESCRIPTION
指定されたパスがディレクトリパスであり、かつ、存在するかどうかを判定する。

.PARAMETER DirectoryPath
ディレクトリパスを指定。

.PARAMETER Help
ヘルプメッセージを表示。

.INPUTS
なし。パイプには対応していない。

.OUTPUTS
System.Boolean。ディレクトリパスが存在する場合にTrue、そうでない場合にFalse。

.EXAMPLE
PS> .\test-d .\Documents\
True

.EXAMPLE
PS> .\test-d .\Documents-non-exists\
False

.LINK
Test-Path
#>

#====================================================================
# 引数
#====================================================================
Param(
    [string]$DirectoryPath,     # ディレクトリを指定
    [switch]$Help           # ヘルプを表示
)

if ($Help -Or -Not $DirectoryPath) {
    Get-Help $PSCommandPath
    exit 1
}

#====================================================================
# ディレクトリパスの存在確認
#====================================================================
if (Test-Path -PathType Container -Path $DirectoryPath) {
    # 指定されたパスはディレクトリであり、存在している。
    $True
} else {
    # 指定されたパスはディレクトリではない。
    if (Test-Path -PathType Leaf -Path $DirectoryPath) {
        # 指定されたパスはファイル。
        $infomsg = "$DirectoryPath : " `
            + "ファイルです。"
    } else {
        # 指定されたパスは存在しない。
        $infomsg = "$DirectoryPath : " `
            + "そのようなディレクトリは存在しません。"
    }
    Write-Host $infomsg
    $False
}

まず、動作から見てみよう。-Helpパラメータを指定する、もしくは引数を何も指定しないと「Get-Help」が内部で呼ばれるようにしてあるため、次のようにヘルプが表示される。

「Get-Help」によるヘルプの出力

直接「Get-Help」を使ってヘルプを表示させることもできる。先ほどと同じ出力が行われていることがわかるだろう。

「Get-Help」で明示的に行ったヘルプの出力

「Get-Help」にパラメータを指定すれば、より詳細なヘルプを表示させることもできる。

「Get-Help -Detailed」でより詳細なヘルプを表示させたところ

「Get-Help」に対応したコメントを追加しただけではなく、次のように-Helpパラメータを処理する部分も書き換えた。前回は、次のように自前で出力するメッセージ(usage)を作成し、それをWrite-Errorコマンドレットで出力させていた。

    $scriptname = Split-Path -Leaf $PSCommandPath
    $errmsg = "usage: $scriptname " `
        + "[-DirectoryPath] ディレクトリパス " `
        + "[-Help]"
    Write-Error $errmsg

今回は、この部分を次のように、内部でGet-Helpコマンドレットを呼ぶ処理へ変更している。

    Get-Help $PSCommandPath

スクリプトが「Get-Help」による出力に対応したのだから、内部から自分を指定して「Get-Help」を実行すれば-Helpパラメータの処理として使えるというわけだ。その分コードは短くなるし、重複する説明を書く必要もなくなり、スマートである。

これまで何度も説明しているように、スクリプトの処理そのものは「Test-Path」で指定されたパスがディレクトリかどうかを判定しているだけだ。しかし、スクリプトとして使えるように、PowerShellの流儀に従って整理していくと、結構なボリュームになってくる。最初にこの辺りをしっかり理解しておけば、今後のスクリプト作成がぐっと楽になるので、今のうちにこういった書き方はマスターしてしまおう。

参考資料