亀の甲羅2

今日もまた朝とく起きて励まなん窓に明るきありあけの月

PowerShell grepみたいなコマンドレット3つ

1.はじめに

Linuxユーザならgrepってかなりの頻度で使うのではないでしょうか? パイプでつなげて、コマンド結果からほしい情報だけを絞り込むために使うと思う。

Get-Aliasの実行結果は以下の通りだが、これのcatに関する情報だけをgrepしようと考えた。

PS C:\Users\hisabo> Get-Alias

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ac -> Add-Content
Alias           asnp -> Add-PSSnapin
Alias           cat -> Get-Content
(以降、略)

PowerShell初心者の私は、PowerShellにおけるgrepSelect-Stringというコマンドレットだということを鵜呑みにして、こんな使い方をした。

PS C:\Users\hisabo> Get-Alias | Select-String -Pattern "cat"

cat

あれ? Get-Aliasのcat行だけが表示されるものと期待していたのだが、そうはならなかった。

これを切っ掛けに、PowerShellにおけるgrepってどうやるの?ということを調べ始めたところ、PowerShellには情報を絞り込むためコマンドレットが(たぶん)3つあることが分かったのでまとめることにした。

2.環境

PowerShellのバージョンは以下の通り。

PS C:\Users\hisabo> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.18362.752
・・・以下、略・・・

3.目指すところ

  • Where-Object の使い方を覚える
  • Select-Object の使い方を覚える
  • Select-String の使い方を覚える

4.やってみよう

とても大切なことなので初めに書いておくが、Get-Aliasコマンドレットは実行結果をテキストで返却してきている訳ではないということを理解しておく必要がある。実行結果はオブジェクト配列(?)という形で返却されてきているのだ(たぶん)。オブジェクトとはデータ(プロパティ)と手続き(メソッド)のカタマリ。オブジェクト配列とはその羅列。このことを理解していると、以下の検証が分かりやすいと思う。

4.1 Where-Object

1つ目のWhere-Objectは、オブジェクト版のgrepと考えていい。

Where-Objectの簡単な例

Where-Objectはパイプで受け取ったオブジェクト配列を絞り込む用途で使う。使い方は、以下のような感じ。

PS C:\Users\hisabo> Get-Alias | Where-Object{ $_.Name -Like "cat"}

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           cat -> Get-Content

Get-Aliasの実行結果のオブジェクト配列を受け取り、Nameプロパティが"cat"の条件に合致するものをWhere-Objectで返却(ここでは出力)している。

Where-ObjectをForeach-Objectで実現する

察しの良い方ならわかると思うが、これはForEach-Object(Aliasは%)で以下のように同じことが実現できる。

PS C:\Users\hisabo> Get-Alias | %{If($_.Name -like "cat"){$_}}

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           cat -> Get-Content

Where-Objectは何を返すコマンドレットなのか?

さて、Where-Objectはテキストを返しているの?オブジェクト配列を返しているの?を実験してみた。

PS C:\Users\hisabo> Get-Alias | Where-Object{ $_.Name -Like "cat"} | %{$_.Definition + " -> " + $_.Name}
Get-Content -> cat

プロパティが指定できるから、Where-Objectはオブジェクト配列を返しているようだ。

Where-Objectは複雑な条件をこなせるのか?

もう少しだけ実験。Where-Objectは比較演算子、論理演算子を使った複雑な条件を作りこめるのか?

PS C:\Users\hisabo> Get-Alias | Where-Object{ $_.Name -Like "cat" -or $_.Name -Like "echo"}

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           cat -> Get-Content
Alias           echo -> Write-Output

ということで、Where-Object内では、Ifと同じように論理演算子を使い、複雑な条件を指定することができる。また、Ifで使われる-eqや-Likeをはじめとした様々な比較演算子を使うことができるようだ。

Get-Aliasの結果をgrepするならオプション使ってもできる

今まで、Get-Aliasの結果を絞り込みしようと頑張ってきたが、Where-Objectを使わずともGet-AliasのNameオプションを使って以下のようにすることもできることを後で知った(笑)。

PS C:\Users\hisabo> Get-Alias -Name cat

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           cat -> Get-Content

4.2 Select-Object

2つ目のSelect-Objectは、パイプで渡されたオブジェクト配列のそれぞれのオブジェクトのプロパティを選択するような使い方が多いようだ。データベースのSELECT句に近いイメージだ。

Select-Objectの簡単な例

Get-AliasのNameプロパティだけを選択して表示させるような使い方ができる。以下のように。

PS C:\Users\hisabo> Get-Alias | Select-Object -Property Name

Name
----
%
?
ac
asnp
cat
cd
CFS
chdir
clc
clear
clhy
(以降、略)

「SELECT obj.Name As Name From `Get-Alias` As obj」みたいな感じ。

Select-ObjectをForeach-Objectで実現する

概ね、同様のことがForeach-Objectで以下のように実現できる。

PS C:\Users\hisabo> Get-Alias | %{$_.Name}
%
?
ac
asnp
cat
cd
CFS
chdir
clc
(以降、略)

Select-Objectは何を返すコマンドレットなのか?

さて、Select-Objectはテキストを返しているの?オブジェクト配列を返しているの?を実験してみた。

PS C:\Users\hisabo> Get-Alias | Select-Object -Property Name,Definition | %{$_.Name + " -> " + $_.Definition}

% -> ForEach-Object
? -> Where-Object
ac -> Add-Content
asnp -> Add-PSSnapIn
cat -> Get-Content
cd -> Set-Location
CFS -> ConvertFrom-String
chdir -> Set-Location
clc -> Clear-Content
clear -> Clear-Host
clhy -> Clear-History
cli -> Clear-Item
clp -> Clear-ItemProperty
cls -> Clear-Host
clv -> Clear-Variable
(以降、略)

どうやら、オブジェクト配列を返しているみたいだ。

4.3 Select-String

3つ目のSelect-Stringはオブジェクトを相手にするのではなく、テキストを相手にするコマンドレット。bashgrepに一番近い。

Select-Stringの簡単な例1(テキストファイルをGrep

PS C:\Users\hisabo> Get-Alias > Get-Alias.txt ・・・Get-Aliasの結果をテキストファイルに出力

PS C:\Users\hisabo> Get-Content Get-Alias.txt ・・・内容確認

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ac -> Add-Content
Alias           asnp -> Add-PSSnapin
Alias           cat -> Get-Content
Alias           cd -> Set-Location
Alias           CFS -> ConvertFrom-String                          3.1.0.0    Microsoft.PowerShell.Utility
Alias           chdir -> Set-Location
Alias           clc -> Clear-Content
(以降、略)

上記のようなテキストファイルを準備した。

PS C:\Users\hisabo> Select-String -Path Get-Alias.txt -Pattern "cat" ・・・テキストファイルをGrep

Get-Alias.txt:8:Alias           cat -> Get-Content

Get-Alias.txt:9:Alias           cd -> Set-Location

(中略)

Get-Alias.txt:109:Alias           pwd -> Get-Location

Get-Alias.txt:142:Alias           sl -> Set-Location

何故か行間に改行が入ってしまうが、一応、テキストファイルのgrepができている。

Select-Stringは何を返すコマンドレットなのか?

PS C:\Users\hisabo> $a = Select-String -Path Get-Alias.txt -Pattern "cat"
PS C:\Users\hisabo> $a.Count
8

配列の個数が8らしい。

PS C:\Users\hisabo> $a[0].Line
Alias           cat -> Get-Content

配列の1つ目のLineプロパティの内容をこんな感じ。Select-Stringはオブジェクト配列を返してきているみたいだ。

PS C:\Users\hisabo> Select-String -Path Get-Alias.txt -Pattern "cat" | Select-Object -Property Line

Line
----
Alias           cat -> Get-Content
Alias           cd -> Set-Location
Alias           chdir -> Set-Location
Alias           gl -> Get-Location
Alias           popd -> Pop-Location
Alias           pushd -> Push-Location
Alias           pwd -> Get-Location
Alias           sl -> Set-Location

Select-Stringの結果をSelect-Objectで処理できるので、どうやらSelect-Stringはオブジェクト配列を返すコマンドレットのようだ。

Select-Stringの簡単な例2(コマンドの結果をGrep

Select-Stringは先に書いたようにテキストを相手にするコマンドレット。別のコマンドの結果をSelect-Stringしたかったら、別のコマンドの結果をテキストストリームに変換する必要がある。コマンド結果をテキストストリームに変換するコマンドがある。

PS C:\Users\hisabo> get-alias | out-string -stream

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           % -> ForEach-Object
Alias           ? -> Where-Object
Alias           ac -> Add-Content
Alias           asnp -> Add-PSSnapin
(略)

out-string -streamはコマンド結果をテキストストリームに変換してくれる。これをカマすことによりSelect-Stringでgrepできる。

PS C:\Users\hisabo> Get-Alias | Out-String -stream | Select-String "cat"

Alias           cat -> Get-Content
Alias           cd -> Set-Location
Alias           chdir -> Set-Location
Alias           gl -> Get-Location
Alias           popd -> Pop-Location
Alias           pushd -> Push-Location
Alias           pwd -> Get-Location
Alias           sl -> Set-Location

こんな風にget-aliasコマンドレットの結果を"cat"を含む行だけをGrepして表示することができる。

終.獲得した知識

  • Where-Objectコマンドレットの使い方
  • Select-Objectコマンドレットの使い方
  • Select-Stringコマンドレットの使い方
  • Out-String -streamでコマンドの結果をテキストストリームに変換できること