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