前回までの取り組みで、「あるディレクトリのパスが存在しているかどうか」を確認できるようになった。成果物(test-d_8.ps1)は次の通りだ。

#!/usr/bin/env pwsh

# 1つの引数が必要。引数はディレクトリパス。
if (0 -eq $args.Length) {
    Write-Host "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
}

今回は、前回追加したメッセージのストリームの調整を行うことにしよう。前回、必要な引数が指定されていない場合と、対象のパスが存在しない場合、対象のパスがディレクトリパスではない場合に、メッセージを出力するようにした。

メッセージの出力には、何も考えずにWrite-Hostコマンドレットを使った。「Write-Host」は「echo」がエイリアスになっており、何かと扱いやすいコマンドレットだからだ。Write-Hostコマンドレットの出力を調べると、次のようにストリーム6で出力されていることがわかる。

Write-Hostはストリーム6を使用

ストリーム6でも良いが、引数がない場合にはどちらかと言えばストリーム2のほうが適切だろう。「$False」になる場合のメッセージは補助的な情報を出していると捉えれば、ストリーム6で良いような気がする。ということで、引数が指定されていない場合のメッセージ出力をストリーム6からストリーム2へ変更しよう。

書き換えと実行

Write-Hostコマンドレットを、Write-Errorコマンドレットに置き換えると、出力先がストリーム6からストリーム2に切り替わる。書き換えた「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
}

動作確認を行うと次のようになる。引数がない場合にはストリーム2、「$False」が返るときのメッセージはストリーム6になっていることを確認できる。

test-d_9.ps1の実行サンプル

これでメッセージの出力がより意図に沿うものになった。

リダイレクトとストリーム

PowerShellではコマンドレットを変更することで出力先のストリームを切り替えるが、UNIXのシェルではリダイレクトを使ってファイルディスクリプタを切り替える。しかし、この方法はPowerShellでは使えない。少なくとも、現在のバージョンでは使えない。

PowerShellのリダイレクトは次のような書式になっている。

演算子 内容
n> ストリームnをファイルへ書き込み
n>> ストリームnをファイルへ追記
n>&1 ストリームnをストリーム1へリダイレクト

ストリームはそれぞれ次のような意味を持っている。

ストリーム番号 内容
1 サクセスストリーム
2 エラーストリーム
3 ワーニングストリーム
4 冗長ストリーミング
5 デバッグストリーム
6 インフォメーションストリーム
* すべてのストリーム

主な使い方をまとめると次のようになる。

演算子 内容
1> サクセスストリームをファイルへ書き込み
1>> サクセスストリームをファイルへ追加
2> エラーストリームをファイルへ書き込み
2>> エラーストリームをファイルへ追加
2>&1 エラーストリームをサクセスストリームへリダイレクト
3> ワーニングストリームをファイルへ書き込み
3>> ワーニングストリームをファイルへ追加
3>&1 ワーニングストリームをサクセスストリームへリダイレクト
4> 冗長ストリームをファイルへ書き込み
4>> 冗長ストリームをファイルへ追加
4>&1 冗長ストリームをサクセスストリームへリダイレクト
5> デバッグストリームをファイルへ書き込み
5>> デバッグストリームをファイルへ追加
5>&1 デバッグストリームをサクセスストリームへリダイレクト
6> インフォメーションストリームをファイルへ書き込み
6>> インフォメーションストリームをファイルへ追加
6>&1 インフォメーションストリームをサクセスストリームへリダイレクト
*> すべてのストリームをファイルへ書き込み
*>> すべてのストリームをファイルへ追加
*>&1 すべてのストリームをサクセスストリームへリダイレクト
> 1>と同じ
>> 1>>と同じ

ここで注目したいのはリダイレクトである「n>&1」という表記だ。これは「指定したストリームnをストリーム1へリダイレクトする」という意味だが、注目する必要があるのはリダイレクト先としてストリーム1しか指定できないことである。

UNIXのシェルであれば「1>&2」という記述で、標準出力を標準エラー出力へリダイレクトできる。PowerShellでもこの機能が使えれば「echo “メッセージ” 6>&2」といった書き方で、ストリーム2へメッセージを書き込めるのだが、現在のPowerShellではこの機能は使うことができない。今後この書き方がサポートされるかどうかは定かではないが、便利な書き方なのでそのうち追加されるのではと筆者は予測している。コミュニティの反応も含めて、今後の展開を見守りたいところだ。

ただメッセージを出力するだけの処理だが、適切なストリームを使うようにすることで、自分でもスクリプトの内容を整理できるようになる。