シェルを使っていてもっとも頻繁に利用するコマンドのひとつがgrepだ。

macOSやLinuxのPowerShell Coreであればアプリケーションとしてgrepを利用できる。しかし、Windowsではgrepは利用できない。

今回はSelect-StringとOut-Stringコマンドレットを使ってgrepのような処理を実現する方法を紹介する。この方法を知っておけばどの環境でも困らずに済むだろう。

Select-String/Out-Stringをgrepのように使う

ファイルやディレクトリの一覧から、必要な行だけを抜き出して表示する例を考えよう。

まず、次のようなディレクトリがあったとする。

ファイルとディレクトリを一覧表示

PS /Users/daichi/Documents/powershell> Get-ChildItem


    Directory: /Users/daichi/Documents/powershell


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2018/05/16     16:51                20180402
d-----       2018/05/16     16:51                20180409
d-----       2018/05/16     16:51                20180416
d-----       2018/05/16     16:51                20180423
d-----       2018/05/16     16:51                20180507
d-----       2018/05/22     18:39                20180514
d-----       2018/05/30     16:23                20180521
d-----       2018/05/30     16:23                20180528
d-----       2018/05/16     16:51                mk
d-----       2018/05/30     16:23                template
d-----       2018/05/16     16:51                tools
------       2018/05/16     16:51           2980 Makefile


PS /Users/daichi/Documents/powershell>

grepであれば次のように使うことでGet-ChildItemコマンドレットの出力から対象となる文字列が含まれている行だけを表示させることができる。

grepアプリケーションで特定の文字列が含まれる行だけを取り出し

PS /Users/daichi/Documents/powershell> Get-ChildItem | grep Make
------       2018/05/16     16:51           2980 Makefile
PS /Users/daichi/Documents/powershell>

PowerShell CoreではSelect-Stringコマンドレットがgrepコマンドと似たような動きをする。

そこでSelect-Stringコマンドレットをgrepと同じように実行すると、次のような結果が得られる。

この場合、表示されている一覧ではなく、一覧に含まれるファイルの中身に一致する行を表示する

PS /Users/daichi/Documents/powershell> Get-ChildItem | Select-String Make

Makefile:58:            ${MAKE} add name=$$(date +%Y%m%d); \
Makefile:66:            ${ECHO}    "''make add name=${TYPESCRIPT_DIRSAMPLE}''"; \
Makefile:72:    @cat template/Makefile | \
Makefile:76:            > ${name}/Makefile
Makefile:86:    ${MAKE} ${USER:S/daichi/build view edit/:S/takasyou/build view/}
Makefile:104:       ${MAKE} ; \
Makefile:105:       ${MAKE} view; \


PS /Users/daichi/Documents/powershell>

Select-StringはGet-ChildItemの出力を一致検索するのではなく、Get-ChildItemの表示した一覧に含まれるファイルの中身を検索して、一致した行を表示している。

PowerShell Coreでは、パイプラインを流れるデータが、標準出力に表示されているテキストではなく、オブジェクトになっているため、このような動きが可能になっている。このあたりの詳しい説明は別の回にするとして、今回はgrepと同じように動作させる方法だけ紹介しておく。
(パイプラインとオブジェクトの基本を知りたい方はObject pipelineUnderstanding the Windows PowerShell Pipelineを参照のこと)

grepと同じ動作を求める場合、Out-Stringというコマンドレットを使う。

Out-Stringというコマンドレットに-Streamというオプションを指定すると、表示されているテキストを行単位で処理させることができるようになる。次のように見た目は一緒だが、出力されるテキストがそのままオブジェクトとして扱われるように変わっている。

Out-String -Streamを利用すると出力された文字列を対象とすることができる

PS /Users/daichi/Documents/powershell> Get-ChildItem | Out-String -Stream


    Directory: /Users/daichi/Documents/powershell


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2018/05/16     16:51                20180402
d-----       2018/05/16     16:51                20180409
d-----       2018/05/16     16:51                20180416
d-----       2018/05/16     16:51                20180423
d-----       2018/05/16     16:51                20180507
d-----       2018/05/22     18:39                20180514
d-----       2018/05/30     16:23                20180521
d-----       2018/05/30     16:23                20180528
d-----       2018/05/16     16:51                mk
d-----       2018/05/30     16:23                template
d-----       2018/05/16     16:51                tools
------       2018/05/16     16:51           2980 Makefile


PS /Users/daichi/Documents/powershell>

Out-String -Streamで処理してからSelect-Stringで検索をかければ、次のようにgrepと同じような結果を得ることができる。

Out-StringとSelect-Stringを使ってgrepと同じような処理をさせた場合

PS /Users/daichi/Documents/powershell> Get-ChildItem | Out-String -Stream | Select-String Make

------       2018/05/16     16:51           2980 Makefile



PS /Users/daichi/Documents/powershell>

Out-String -Streamはよく使う処理なので同様の処理を行うossという関数が用意されている。Select-Stringにはslsというエイリアスが用意されており、Get-ChildItemにはdirというエイリアスが用意されている。

つまり、先ほどの処理は次のように書くことができる。

エイリアスを使って書くとこうなる

PS /Users/daichi/Documents/powershell> dir | oss | sls Make
------       2018/05/16     16:51           2980 Makefile



PS /Users/daichi/Documents/powershell>

つまり、出力を「| oss | sls キーワード」に接続すればgrepのように一致検索を実施することができるというわけだ。これは便利な機能なので覚えておこう。

正規表現で一致指定

Select-Stringは引数のキーワードに正規表現を使うことができる。たとえば次のように正規表現を使えば連続する8個の数字を含んだ行が表示されるようになる。

正規表現を使った文字列の一致

PS /Users/daichi/Documents/powershell> dir | oss | sls '[0-9]{8}'

d-----       2018/05/16     16:51                20180402

d-----       2018/05/16     16:51                20180409

d-----       2018/05/16     16:51                20180416

d-----       2018/05/16     16:51                20180423

d-----       2018/05/16     16:51                20180507

d-----       2018/05/22     18:39                20180514

d-----       2018/05/30     16:23                20180521

d-----       2018/05/30     16:23                20180528



PS /Users/daichi/Documents/powershell>

PowerShell Coreでは次の正規表現を利用できる。UNIX系のコマンドで利用できる正規表現とほとんど同じだ。

正規表現(文字一致) 意味
. 任意の1文字
^ 行頭
$ 行末
\文字 エスケープ
[文字達] ブランケット内の1文字に一致
[^文字達] ブランケット内に存在しない1文字に一致
[始-終] 範囲指定でのブランケット
\w 文字に一致
\W 文字以外に一致
\s ホワイトスペースに一致
\S ホワイトスペース以外に一致
\d 数字に一致
\D 数字以外に一致
正規表現(繰り返し) 意味
? 0回または1回の繰り返し
* 0回以上の繰り返し
+ 1回以上の繰り返し
{n} n回繰り返し
{n,} n回以上繰り返し
{n,m} n回以上m回以下繰り返し

 

正規表現を使わない

キーワードを正規表現として理解させたくない場合には、次のように-SimpleMatchオプションを指定する。

正規表現を使わないようにする-SimpleMatch

PS /Users/daichi/Documents/powershell> dir | oss | sls '[0-9]{8}' -SimpleMatch
PS /Users/daichi/Documents/powershell>

 

一致しない行を出力

一致しない行だけを出力させたいことがある。

例えば空行やコメント行以外を表示させる、といった場合だ。

その場合には、次のように-NotMatchオプションを指定する。

一致しない行を出力させる場合には-NotMatchオプションを指定する

PS /Users/daichi/Documents/powershell> dir | oss | sls '[0-9]{8}' -NotMatch



    Directory: /Users/daichi/Documents/powershell


Mode                LastWriteTime         Length Name

ーーーー                ーーーーーーーーーーーーー         ーーーーーー ーーーー

dーーーーー       2018/05/16     16:51                mk

dーーーーー       2018/05/30     16:56                template

dーーーーー       2018/05/16     16:51                tools

ーーーーーー       2018/05/16     16:51           2980 Makefile





PS /Users/daichi/Documents/powershell>

 

前後の行も出力

一致した行の前後も表示させたいことがあるが、そういった場合には-Contextオプションを使用する。

オプションに与えた数値行分だけ前後も表示されるようになる。

前後の行も出力させる-Context

PS /Users/daichi/Documents/powershell> dir | oss | sls 20180507 -Context 3

  dーーーーー       2018/05/16     16:51                20180409

  dーーーーー       2018/05/16     16:51                20180416

  dーーーーー       2018/05/16     16:51                20180423

> dーーーーー       2018/05/16     16:51                20180507

  dーーーーー       2018/05/22     18:39                20180514

  dーーーーー       2018/05/30     16:23                20180521

  dーーーーー       2018/05/30     16:23                20180528



PS /Users/daichi/Documents/powershell>

 

大文字と小文字を区別

Select-Stringはデフォルトではキーワードの大文字と小文字を区別しないで動作する。明示的に大文字小文字を区別して一致検索させる場合には-CaseSensitiveオプションを指定する。

大文字と小文字を区別する-CaseSensitive

PS /Users/daichi/Documents/powershell> dir | oss | sls 'make' -CaseSensitive
PS /Users/daichi/Documents/powershell>

 

ファイルの中身を検索

パイプライン経由でデータを流し込むのではなく、直接ファイルの中身を検索させる場合には第2引数にパスを指定する。

第2引数にファイルパスを指定してファイルの中身を検索

PS /Users/daichi/Documents/powershell> sls make Makefile

Makefile:58:            ${MAKE} add name=$$(date +%Y%m%d); \
Makefile:66:            ${ECHO}    "''make add name=${TYPESCRIPT_DIRSAMPLE}''"; \
Makefile:72:    @cat template/Makefile | \
Makefile:76:            > ${name}/Makefile
Makefile:86:    ${MAKE} ${USER:S/daichi/build view edit/:S/takasyou/build view/}
Makefile:104:       ${MAKE} ; \
Makefile:105:       ${MAKE} view; \


PS /Users/daichi/Documents/powershell>

複数のファイルを指定したい場合には第2引数に指定するパスをカンマ区切りで追加していけばよい。

パイプラインに慣れる

Out-String -Streamを使って表示される文字列を処理していく方法は、これまでのシェルやコマンドプロンプトでの処理とよく似ている。どういったデータが処理されるのかわりやすいので、はじめてこうしたCUIに触れるという場合にもわかりやすい方法ではないかと思う。

PowerShell Coreのパイプラインは、表示されるテキストデータではなく、オブジェクトそのものを渡す機能であるため、実際にはもっと複雑なことができる。見た目のテキストを処理する方法に慣れていただいた後は、こうしたオブジェクトを加味した処理を行う方法も取り上げていく予定だ。

参考資料