LINQっぽいこと

PowerShellでLINQ(SQLのような操作でオブジェクトを抽出する)のようなことができれば、繰り返し処理や変数の詰め替えなどしなくてもリストの検索などができるようになります。
前提
C:\aaa\ には以下のファイルが存在する
c:\aaa\aaa.txt
c:\aaa\bbb.csv
c:\aaa\bbb_ccc.csv
c:\aaa\ccc.csv
Where
[array]$list = Get-ChildItem "c:\aaa\" | Where-Object {
$_.Extension -eq ".csv" -and $_.BaseName -like "bbb*"
}
$list
結果
c:\aaa\bbb.csv
c:\aaa\bbb_ccc.csv
Where-Objectは、パイプラインで受け取ったオブジェクトのリストのうち、条件に一致したオブジェクトだけを抽出します。
上記の例だとGet-ChildItemが返却するFileSystemInfoオブジェクトのリストのうち、拡張子(Extension)が.csvで、かつ拡張子を除くファイル名(BaseName)が”bbb*”と一致するオブジェクトのリストを取得します。
Select
[array]$list = Get-ChildItem "c:\aaa\" `
| Select-Object -Property @("BaseName","Extension")
foreach ($data in $list) {
Write-Output "base:$($data.BaseName) ex:$($data.Extension)"
}
結果
base:aaa ex:.txt
base:bbb ex:.csv
base:bbb_ccc ex:.csv
base:ccc ex:.csv
Select-Objectは、パイプラインで渡されたオブジェクトのリストからオブジェクトを取得し、-Propertyで指定したプロパティを取得し、新たなオブジェクトとして生成します。
-Propertyで指定したプロパティが1つだけであっても新たなオブジェクトを生成します(stringで返却されるわけではない)。
また、Select-Object -First 1 や Select-Object -Last 1 などでリストの先頭やリストの最終行の取得が行え、Select-Object -Skip 1やSelect-Object -SkipLast 1 などで先頭行や最終行以外の取得が行えます。
Selectの亜流
[array]$list = (Get-ChildItem "c:\aaa\").BaseName
$list
結果
aaa
bbb
bbb_ccc
ccc
本来はGet-ChildItemの返却値はオブジェクトの配列なので、配列のプロパティにBaseNameは存在しませんが、PowerShellでは配列の中身のオブジェクトのプロパティも検索してくれ、かつ、LinqのSelectのように配列のオブジェクトのBaseNameのみを取得し、BaseName(string)の配列として返却してくれます。
これをPowerShellらしいと言えるかは微妙で、個人的にはプログラムの可読性が下がると思うので避けたほうがよいと考えています。
上記の例だと[array]があるのでまだ配列で返却されるというのがわかりますが、[array]は省略可能で、その場合ぱっと見、配列が返却されているようにみえません。
Get-ChildItemは仕様がわかっているからそのようなミスリードは少ないですが、Get-ChildItemの代わりに自作の関数だった場合はミスリードは多くなります。
多少長くなりますが、ForEach-Objectを使ってリストを作成したほうがわかりやすいと思います。
$base_name_list = Get-ChildItem "c:\aaa\" | ForEach-Object {
$_.BaseName
}
Order by
Get-ChildItem "c:\aaa" | Sort-Object -Property @("Extension","Name")
結果
c:\aaa\bbb.csv
c:\aaa\bbb_ccc.csv
c:\aaa\ccc.csv
c:\aaa\aaa.txt
GroupとSum
$list = Get-ChildItem "c:\aaa\" | Group-Object -Property "Extension"
$list
$list[1].Group
結果
Count Name Group
----- ---- -----
1 .txt {aaa.txt}
3 .csv {bbb.csv, bbb_ccc.csv, ccc.csv}
ディレクトリ: E:\Users\nakadate\Documents
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2020/09/06 1:53 100 bbb.csv
-a---- 2020/09/06 1:53 200 bbb_ccc.csv
-a---- 2020/09/06 1:54 300 ccc.csv
どーゆーことかというと、Group-Objectは、パイプラインのオブジェクトをGroupのキーごとに仕分けしてオブジェクトリストを作成します。
Sumは、
$list = Get-ChildItem "c:\aaa\" | Group-Object -Property "Extension"
$list[1].Group | Measure-Object -Sum length
"$($list[1].name) $(($list[1].Group `
| Measure-Object -Sum length).Sum)"
結果
Count : 3
Average :
Sum : 600
Maximum :
Minimum :
Property : Length
.csv 600
拡張子ごとにリスト化されたFileInfoオブジェクトをMeasure-Object -Sumで集計します。
上記のコードでは.csvの1行分しか出力できていませんが、以下のように記述すると一度にGroup ByとSumができます。
$list = Get-ChildItem "c:\aaa\" `
| Group-Object -Property "Extension" `
| Select-Object Name, @{Name="TotalLength";Expression={($_.Group | Measure-Object -Sum Length).Sum}}
$list
結果
Name TotalLength
---- -----------
.txt 100
.csv 600