今回はこれまで作ってきたスクリプトを、PowerShellの提供機能を使ってPowerShell風に整理していく。無理やりUNIX系のシェルスクリプトのように書くこともできるが、まずはその言語が提供している機能で書いておくほうが無難だ。今回は、引数としてパラメータを使う場合の処理を書いていく。

前回作ったスクリプトの”問題”

前回作ったスクリプト(test-d_9.ps1)は次の通りだ。

#!/usr/bin/env pwsh

# 1つの引数が必要。引数はディレクトリパス。
if (0 -eq $args.Length) {
    Write-Error "usage: test-d_2.ps1 ディレクトリパス"
    exit 1
}

if (Test-Path -PathType Container -Path $args[0]) {
    # 指定されたパスはディレクトリ
    $True
} else {
    # 指定されたパスはディレクトリではない
    if (Test-Path -PathType Leaf -Path $args[0]) {
        # 指定されたパスはファイル
        Write-Host "$args[0]: ファイルです。"
    } else {
        # 指定されたパスは存在しない
        Write-Host "$args[0]: そのようなディレクトリは存在しません。"
    }
    $False
}

実行すると次のようになる。

実行サンプル

いくつか問題があるのだが、まずはusageの出力に注目してみよう。スクリプトの名前が「test-d_2.ps1」になっている。実際には「test-d_9.ps1」だ。これはスクリプト名をハードコーディングしてあるため、この部分の書き換えを忘れたことで発生したバグということになる。

まずはこういった細かい部分を書き換えていく。

スクリプト名を取り出して利用する

スクリプトパスが「$PSCommandPath」という変数に収められている。これを使ってusageを出力するように変更したのが次の「test-d_10.ps1」だ。

#!/usr/bin/env pwsh

$scriptname = $PSCommandPath

# 1つの引数が必要。引数はディレクトリパス。
if (0 -eq $args.Length) {
    Write-Error "usage: $scriptname ディレクトリパス"
    exit 1
}

if (Test-Path -PathType Container -Path $args[0]) {
    # 指定されたパスはディレクトリ
    $True
} else {
    # 指定されたパスはディレクトリではない
    if (Test-Path -PathType Leaf -Path $args[0]) {
        # 指定されたパスはファイル
        Write-Host "$args[0]: ファイルです。"
    } else {
        # 指定されたパスは存在しない
        Write-Host "$args[0]: そのようなディレクトリは存在しません。"
    }
    $False
}

実行すると次のようになる。

実行サンプル

スクリプト名が絶対パスで表示されていることがわかる。これでは使いにくいので、スクリプトパスからスクリプト名だけ取り出して使うように、次のように書き換える(test-d_11.ps1)。

#!/usr/bin/env pwsh

$scriptname = Split-Path -Leaf $PSCommandPath

# 1つの引数が必要。引数はディレクトリパス。
if (0 -eq $args.Length) {
    Write-Error "usage: $scriptname ディレクトリパス"
    exit 1
}

if (Test-Path -PathType Container -Path $args[0]) {
    # 指定されたパスはディレクトリ
    $True
} else {
    # 指定されたパスはディレクトリではない
    if (Test-Path -PathType Leaf -Path $args[0]) {
        # 指定されたパスはファイル
        Write-Host "$args[0]: ファイルです。"
    } else {
        # 指定されたパスは存在しない
        Write-Host "$args[0]: そのようなディレクトリは存在しません。"
    }
    $False
}

実行すると次のようになる。

実行サンプル

usageに使われるスクリプト名が適切に出力されるようになったことがわかる。

こういった書き換えは結構重要だ。なぜかと言うと、結局こういうコードはコピー&ペーストで使い回すことが多いので、名前などがハードコーディングされていると書き換えられず使われてしまうケースが多いからだ。上記のように、コピー&ペーストしてもうまく機能するようにしておけば、将来の自分が楽になる。

パラメータを使えるようにする

さて、今回の主題はパラメータだ。このスクリプトでパラメータを指定できるようにしたい。PowerShellでは「Param()」がパラメータの処理を行うために使われるので、これを使ってスクリプトを書き換える。まずは、usageを表示する処理を明示的にパラメータで実行できるようにする。「-Help」でusageが出力されるようにしてみよう。書き換えたスクリプト(test-d_12.ps1)は次の通りだ。

#!/usr/bin/env pwsh

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

if ($Help -Or (0 -eq $args.Length)) {
    $scriptname = Split-Path -Leaf $PSCommandPath
    Write-Error "usage: $scriptname [-Help]"
    exit 1
}

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

実行すると次のようになる。

実行サンプル

「-Help」でusageが出力されていることを確認できる。

パラメータの処理を自前で行うこともできるが、「Param()」を使うと、PowerShellの「Tab」キーによる補完機能が使用できるようになる。例えば、今回の例であれば「-」と入力した段階で「Tab」キーを押すと、「-Help」まで入力が補完される。そのほか便利な機能も自動的に動き出すので、PowerShellの流儀に従って書いておくと何かと便利なのである。

さらにパラメータを使う

さらにもう少しパラメータを使うように書き換えてみよう。これまではディレクトリパスを引数として指定していたが、これをパラメータで指定できるようにする。-DirectoryPathでディレクトリパスを指定できるようにしよう。指定順序も考慮すると、次のように書いておけば、-DirectoryPathパラメータを指定しても、これまでのようにディレクトリパスだけを指定しても実行できるようになる(test-d_13.ps1)。

#!/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
}

実行すると次のようになる。

実行サンプル

かなりPowerShellらしい動作をするようになってきた。「Tab」キーによる補完機能は動作するし、パラメータ指定と順位による指定が機能している。

このように、PowerShellのスクリプトにはある程度決まったパターンがある。この書き方を最初に覚えてしまえば、ある程度体裁の整ったスクリプトを作ることができる。最初は小さい機能を作るところから、スクリプトの書き方を覚えていくとよいだろう。