リダイレクト演算子

UNIX系のシェルを扱う方なら、リダイレクトを使ったことがあるだろう。

もっとも簡単なリダイレクトは「>」だ。この記号を使った場合、記号の右側に書いたファイルパスに記号の左側に書いたコマンドの標準出力がリダイレクトされるようになる。ようするにコマンドの出力がファイルに書き込まれるようになる。「>>」だと新規書き込みではなく追記といった処理になる。

PowerShell Coreにもほとんど同じ処理を行うための記号として「リダイレクト演算子」が用意されている。使い方はシェルのリダイレクトとほとんど同じだ。

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

リダイレクト演算子には次のようにストリーム番号を指定することができる。リダイレクト演算子を指定しなかった場合、デフォルトの値として1が指定されたものとして処理が行われる。

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

ストリーム番号には次のようなものが用意されている。シェルの標準入力や標準エラー出力に相当するものだと考えておけばよいと思う。標準出力のファイルディスクリプタは1、標準エラー出力のファイルディスクリプタは2であり、PowerShell Coreのストリーム番号と一致している。故意に同じにしてあるのだと思う。

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

シェルとPowerShell Coreの大きな違いは、シェルでは1 (標準出力)と2(標準エラー出力)のみを扱うことがほとんどであるのに対して、PowerShell Coreでは1から6までストリームが用意されている点にある。1と2に関してはシェルもPowerShell Coreもほとんど同じだ。ストリーム番号3以上が用意されている点がPowetShell Coreの独自と言える。出力に関して意味ごとにより細かく出力を制御できるようになっていることがわかる。

ストリーム番号とリダイレクト演算子の組み合わせを列挙すると次のようになる。

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

「>」と「>>」に関してはわかりやすいと思うが、「>&1」はちょっとわかりにくいかもしれない。シェルの場合にはシステムコールを使って指定したファイルディスクリプタを標準出力のファイルディスクリプタで上書き……というかコピーする処理に相当するのだが、PowerShell Coreの場合にもほとんど同じ動きをする。PowerShell Coreの場合にはストリームのコピーということになる。

これは例を考えると理解しやすい。「3>&1」といったリダイレクト演算子を指定した場合、ワーニングストリームへの出力がサクセスストリームへの出力に変わる。これはエラーも含めて標準出力に流したいといった場合に利用する。

リダイレクト演算子の使用例

リダイレクト演算子のもっとも基本的な使い方は「>」でサクセスストリーム(標準出力)のリダイレクト、「2>」でエラーストリーム(標準エラー出力)のリダイレクトだろう。シェルやUNIX環境における「/dev/null」のような処理はPowerShell Coreでは「$null」で機能するので、次のような方法で基本的なリダイレクト演算子の実行を確認することができる。

サクセスストリームのリダイレクトを確認

PS /Users/daichi> echo "Hello World"
Hello World
PS /Users/daichi> echo "Hello World" > $null
PS /Users/daichi> echo "Hello World" 2> $null
Hello World
PS /Users/daichi>

故意にエラーメッセージを表示させれば、次のようにエラーストリームのリダイレクトを確認することができる。

エラーストリームのリダイレクトを確認

PS /Users/daichi> dir C:/Hello
dir : Cannot find drive. A drive with the name 'C' does not exist.
At line:1 char:1
+ dir C:/Hello
+ ~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (C:String) [Get-ChildItem], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS /Users/daichi> dir C:/Hello > $null
dir : Cannot find drive. A drive with the name 'C' does not exist.
At line:1 char:1
+ dir C:/Hello > $null
+ ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (C:String) [Get-ChildItem], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS /Users/daichi> dir C:/Hello 2> $null
PS /Users/daichi>

サクセスストリームとエラーストリームの双方に対してメッセージが流れるようにコマンドレットを実行してやると、それぞれ対象だけがリダイレクトされていることがよくわかる。

サクセスストリームとエラーストリームの双方に送られるコマンドレットの結果をリダイレクト

PS /Users/daichi> dir '/', 'C:\'


    Directory: /

Mode                LastWriteTime         Length Name
‐‐‐‐                ‐‐‐‐‐‐‐‐‐‐‐‐‐         ‐‐‐‐‐‐ ‐‐‐‐
d‐r‐‐‐        2019/05/07    15:17                Applications
d‐r‐‐‐        2019/04/06     4:56                bin
d‐r‐‐‐        2018/09/28    23:26                cores
d‐r‐‐‐        2019/04/06     4:58                dev
d‐r‐‐l        2018/09/28    23:23                etc
d‐r‐‐‐        2019/05/07    16:12                home
d‐r‐‐‐        2016/10/03     3:38                Incompatible Software
d‐r‐‐‐        2018/09/29    10:25                Library
d‐r‐‐‐        2019/05/07    16:12                net
d‐r‐‐‐        2018/09/28    23:26                Network
d‐r‐‐‐        2018/09/28    23:26                private
d‐‐‐‐‐        2017/11/28     9:16                root
d‐r‐‐‐        2019/04/06     4:56                sbin
d‐r‐‐‐        2018/09/21     0:05                System
d‐r‐‐l        2018/09/28    23:24                tmp
d‐r‐‐‐        2018/09/28    23:26                Users
d‐r‐‐‐        2018/09/21     0:01                usr
d‐r‐‐l        2018/09/28    23:24                var
d‐r‐‐‐        2019/05/07    17:25                Volumes
d‐r‐‐l        2012/08/14    10:20                ユーザ情報
‐‐r‐‐‐        2018/08/17    20:55            313 installer.failurerequests
dir : Cannot find drive. A drive with the name 'C' does not exist.
At line:1 char:1
+ dir '/', 'C:\'
+ ~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (C:String) [Get‐ChildItem], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand


PS /Users/daichi>

サクセスストリームがリダイレクトされ、エラーストリームだけが表示されている

PS /Users/daichi> dir '/', 'C:\' > $null
dir : Cannot find drive. A drive with the name 'C' does not exist.
At line:1 char:1
+ dir '/', 'C:\' > $null
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (C:String) [Get-ChildItem], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

PS /Users/daichi>

エラーストリームがリダイレクトされ、サクセスストリームだけが表示されている

PS /Users/daichi> dir '/', 'C:\' 2> $null


    Directory: /

Mode                LastWriteTime         Length Name
‐‐‐‐                ‐‐‐‐‐‐‐‐‐‐‐‐‐         ‐‐‐‐‐‐ ‐‐‐‐
d‐r‐‐‐        2019/05/07    15:17                Applications
d‐r‐‐‐        2019/04/06     4:56                bin
d‐r‐‐‐        2018/09/28    23:26                cores
d‐r‐‐‐        2019/04/06     4:58                dev
d‐r‐‐l        2018/09/28    23:23                etc
d‐r‐‐‐        2019/05/07    16:12                home
d‐r‐‐‐        2016/10/03     3:38                Incompatible Software
d‐r‐‐‐        2018/09/29    10:25                Library
d‐r‐‐‐        2019/05/07    16:12                net
d‐r‐‐‐        2018/09/28    23:26                Network
d‐r‐‐‐        2018/09/28    23:26                private
d‐‐‐‐‐        2017/11/28     9:16                root
d‐r‐‐‐        2019/04/06     4:56                sbin
d‐r‐‐‐        2018/09/21     0:05                System
d‐r‐‐l        2018/09/28    23:24                tmp
d‐r‐‐‐        2018/09/28    23:26                Users
d‐r‐‐‐        2018/09/21     0:01                usr
d‐r‐‐l        2018/09/28    23:24                var
d‐r‐‐‐        2019/05/07    17:25                Volumes
d‐r‐‐l        2012/08/14    10:20                ユーザ情報
‐‐r‐‐‐        2018/08/17    20:55            313 installer.failurerequests

PS /Users/daichi>

Write-Warningコマンドレットを使用するとワーニングストリームに対してメッセージを送ることができ、次のように「3>」でワーニングストリームだけがリダイレクトできることを確認できる。「*>」はすべてのストリームに対して適用されるので、ワーニングストリームについてもリダイレクトを確認できる。

Write-Warningコマンドレットでワーニングストリームに対してリダイレクトを送信

PS /Users/daichi> Write-Warning 'Hello'
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' > $null
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' 1> $null
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' 2> $null
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' 3> $null
PS /Users/daichi> Write-Warning 'Hello' 4> $null
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' 5> $null
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' 6> $null
WARNING: Hello
PS /Users/daichi> Write-Warning 'Hello' *> $null
PS /Users/daichi>

次のような書き方をすると、複数の種類のストリームに対してメッセージを送信し、それらをリダイレクトする方法を試すことができる。書き方としては「3>&1 6>&1 > $null」に注目しておきたい。

複数のストリームに対してメッセージを出力し、それらをリダイレクトするサンプル

PS /Users/daichi> &{ Write-Warning 'Hello'; Write-Information 'World'; }
WARNING: Hello
PS /Users/daichi> &{ Write-Warning 'Hello'; Write-Information 'World'; } 6>&1
WARNING: Hello
World
PS /Users/daichi> &{ Write-Warning 'Hello'; Write-Information 'World'; } 3>&1 6>&1
WARNING: Hello
World
PS /Users/daichi> &{ Write-Warning 'Hello'; Write-Information 'World'; } 3>&1 6>&1 > $null
PS /Users/daichi>

「3>&1 6>&1 > $null」で、まずワーニングストリームがサクセスストリームとなり、次にインフォメーションストリームがサクセスストリームになっている。最後の「> $null」でサクセスストリームが「$null」にリダイレクトされるので、すべての出力が消えることになる。こうした書き方はシェルやシェルスクリプトではよく用いられるが、PowerShell Coreでも同じような書き方を行うことができる。

ファイルへのリダイレクトはリダイレクト演算子を使わずとも、Out-Fileコマンドレットを使っても行うことができる。しかし、リダイレクト演算子で同じことが可能で、シェルやシェルスクリプトと書き方も同じなので、こちらを覚えておいた方が便利だろう。

参考資料