PowerShellでちょっとしたスクリプトを書こうとして配列に要素を追加する書き方をど忘れしてしまうことってありませんか。自分用の備忘録も兼ねて配列の作成から取得、追加、削除、検索、そしてforeachでのループ処理までを一通りメモメモしました。
- OS
Windows 11 Home 25H2
- pwsh.exe
7.6.5
- powershell.exe
5.1.26100.9278
配列操作の早見表
| やりたいこと | 書き方 |
|---|---|
| 配列を作る | $a = @("A","B","C") |
| 空の配列を作る | $a = @() |
| 連番の配列を作る | $a = 1..10 |
| 先頭の要素を取得する | $a[0] |
| 末尾の要素を取得する | $a[-1] |
| 要素数を確認する | $a.Count |
| 要素を追加する | $a += "D" |
| 要素を削除する | $a = @($a | Where-Object { $_ -ne "B" }) |
| 値が含まれるか確認する | $a -contains "B" |
| 条件で絞り込む | $a | Where-Object { $_ -like "A*" } |
| 重複を除いて並べ替える | $a | Sort-Object -Unique |
| 順番に処理する | foreach ($x in $a) { Write-Output $x } |
PowerShellの配列とは
配列は複数の値をひとつの変数にまとめて管理できる仕組みです。フォルダ内のファイル名を1つずつ別々の変数に入れる代わりに、配列にまとめて入れておけば後からforeachなどで一括処理できます。
$fruits = @("Apple", "Banana", "Orange")@()は配列を作るための記号(配列サブ式演算子)です。中の値をカンマで区切って並べればそれだけで配列になります。$fruits.GetType().FullNameを実行するとSystem.Object[]と表示され、配列として扱われていることが確認できます。
$を付けた変数への代入やGetType()による型の確認から整理したい場合は変数の基本操作をまとめた記事も参考にしてください。


配列を作成する方法
複数の値から配列を作る
$fruits = @("Apple", "Banana", "Orange")実は@()を省略して次のように書いても配列になります。
$fruits = "Apple", "Banana", "Orange"@()を付けたほうが「配列を作っている」という意図が伝わりやすいので、筆者的には付けたほうが良いんじゃないかなと思います。
空の配列を作る
$empty = @()これから+=などで要素を追加していく土台として使うことが多いです。
数値の連続した配列を作る
$numbers = 1..10..は範囲演算子と呼ばれ、1から10までの連続した整数が入った配列を一発で作れます。10..1のように書けば降順の配列になります。
1要素でも配列として扱う
下記のように記載すると$singleは配列ではなく文字列型になりますよね。
$single = "Apple"要素が1つでも配列にしたいときは@()か、先頭にカンマを付ける書き方を使います。
$single = @("Apple")
$single = ,"Apple"配列の要素を取得・変更する
インデックスは0から始まります。先頭は[0]、末尾は[-1]で取得できます。
$fruits[0]
$fruits[-1]Apple
Orange値の書き換えも代入するだけです。
$fruits[1] = "Grape"複数の要素をまとめて取り出したいときはインデックスをカンマで並べるか範囲を指定します。
$fruits[0,2]
$fruits[0..1]配列の要素数を確認する
要素数の確認には.Count(または.Length)を使います。
$fruits.Countコマンドの結果をそのまま変数へ代入すると1件だけ返った場合は配列ではなく、そのオブジェクト自体が入ることがあります。PowerShell 3.0以降は多くの単一オブジェクトでも.Countを使えますが、Windows PowerShell 5.1のPSCustomObjectなどには例外があります。件数にかかわらず配列として扱いたい場合は、コマンドの実行結果を@()で囲んで受け取ります。
配列が空(変数としては存在しているが内容が全くない)かどうかを判定したいときは次のように書きます。
if ($fruits.Count -eq 0) {
Write-Output "配列は空です"
}配列へ要素を追加する
要素の追加には+=を使います。
$fruits += "Grape".NETの配列はサイズが固定です。固定とはいっても追加できないワケではないですが、Windows PowerShell 5.1やPowerShell 7.4以前では+=による配列への追加を繰り返すと配列の作り直しとコピーが処理の負担になります。
PowerShell 7.5ではオブジェクト配列への+=が最適化されました。「大量に追加するなら必ずListの方が速い」とは言い切れません。追加や削除をメソッドで扱いたい場合はSystem.Collections.Generic.Listが便利です。
$list = [System.Collections.Generic.List[string]]::new()
$list.Add("Apple")
$list.Add("Banana")新しく可変長のコレクションを使う場合MicrosoftはArrayListよりList<T>を推奨しています。性能を比較するときは、使用するPowerShellのバージョンと実際の処理内容を揃えて確認してください。
配列の追加、Listの追加動作の実測比較をしてみた
同じPCでWindows PowerShell 5.1とPowerShell 7.6.5を動かし、配列への+=とListへの追加を比較しました。今回の環境では、7.6.5の+=は5.1より大幅に短時間になったものの、Listへの追加の方が速い結果でした。
CPUはAMD Ryzen 7 9700Xを使用し、両方とも64bitプロセスで実行しています。
| 項目 | 検証条件 |
|---|---|
| 入力データ | あらかじめ作成した文字列入りの object[] |
| Listの型 | System.Collections.Generic.List[object] |
| 計測回数 | 各件数・各方式で2回ウォームアップ後、8回計測 |
| 掲載値 | 処理時間の中央値。単位はミリ秒 |
入力データの生成、画面表示、結果の検証、CSV保存は計測に含めていません。各試行の前にGCを実行し、方式の実行順を入れ替えています。Listから配列へ変換する時間も対象外です。
Windows PowerShell 5.1の結果
| 作成方法 | 1,000件 | 10,000件 | 30,000件 |
|---|---|---|---|
配列へ+=で追加 | 10.155 | 1,233.550 | 13,410.610 |
ListへAdd()で追加 | 0.045 | 0.489 | 1.480 |
| Listの初期容量を指定して追加 | 0.048 | 0.472 | 1.445 |
foreachの出力をまとめて配列へ格納 | 0.042 | 0.435 | 1.355 |
5.1では+=を繰り返した場合の所要時間が件数とともに大きく増えました。30,000件では約13.4秒かかっており、ループ内で大量に追加する処理では差がはっきり出ています。
PowerShell 7.6.5の結果
| 作成方法 | 1,000件 | 10,000件 | 30,000件 |
|---|---|---|---|
配列へ+=で追加 | 0.235 | 11.212 | 279.531 |
ListへAdd()で追加 | 0.193 | 2.656 | 2.587 |
| Listの初期容量を指定して追加 | 0.195 | 2.645 | 2.573 |
foreachの出力をまとめて配列へ格納 | 0.036 | 0.557 | 0.482 |
7.6.5では30,000件の+=が約280ミリ秒で完了しました。5.1の約13.4秒と比べると所要時間は約48分の1です。
同じ7.6.5のListへの追加は約2.59ミリ秒で+=はその約108倍の時間がかかりました。今回の測定ではListの初期容量を指定しても、通常のListへの追加と大きな差はありませんでした。
比較した4方式のうち、各条件で中央値が最も小さかったのはforeachの出力をまとめて配列へ格納する方法です。ループで値を生成して最後にまとめて受け取る用途ではこの書き方も選択肢になります。
7.6.5のListなどでは10,000件より30,000件の中央値が小さくなる箇所があり、試行間のばらつきも確認できました。短時間の処理の小さな差や、件数に対する増加率は参考として見てください。また、5.1と7.6.5では.NETなどの実行環境も異なるため、バージョン間の差をすべて7.5の最適化だけによる効果とは断定できません。
今回の環境ではループ内で大量に追加する用途にはListが有利でした。
手元のPowerShellのバージョン確認やWindows PowerShell 5.1と併用する7系の導入手順はこちらの記事で確認できます。


配列から要素を削除する
PowerShellの通常の配列には特定の要素だけを直接削除するメソッドがありません。配列自体がサイズ固定のオブジェクトだからです。実務ではWhere-Objectで対象を除外した新しい配列を作り直す形が基本になります。
$fruits = @("Apple", "Banana", "Orange")
$fruits = @($fruits | Where-Object { $_ -ne "Banana" })この書き方は値が「Banana」の要素をすべて除外します。結果を@()で囲んでいるため、残る要素が0件や1件でも配列として扱えます。
削除前後を比べるとこんな感じです。
Apple
Banana
OrangeApple
Orange追加・削除を頻繁に繰り返す配列であれば、先ほど紹介したListを使うとRemove()やRemoveAt()でそのまま削除できるので扱いやすくなります。
$list.Remove("Banana")配列から値を検索・抽出する
検索は「存在するかどうかを確認したいだけ」なのか「条件に合う要素を取り出したいのか」で使うものが変わります。ごちゃまぜにすると回りくどいコードになりがちなので、ここは分けて覚えておくのがおすすめです。
| 目的 | 使うもの | 戻り値 |
|---|---|---|
| 値が含まれるか確認する | -contains | True / False |
| 値が配列に含まれるか確認する(左右逆) | -in | True / False |
| 条件に合う要素を取り出す | Where-Object | 条件に合う要素(複数可) |
| パターンに一致する要素を取り出す | -match | 一致した要素(複数可) |
| 値の位置(インデックス)を調べる | [Array]::IndexOf() | インデックス番号(見つからなければ-1) |
存在確認
存在確認だけなら-containsが手軽です。
$fruits = @("Apple", "Banana", "Orange")
$fruits -contains "Apple"-inは調べたい値を左側、配列を右側に書きます。1つの値が配列に含まれるかを確認する演算子です。
"Apple" -in $fruits条件に合う要素を抽出する
条件付きで絞り込みたいときはWhere-Objectを使います。
$fruits | Where-Object { $_ -like "A*" }正規表現を使う
正規表現で調べたいときは-matchが使えます。配列に対して使うとパターンに一致した要素だけが返ってきます。
$fruits -match "an"要素の位置を把握する
位置そのものが知りたいときは[Array]::IndexOf()です。
[Array]::IndexOf($fruits, "Banana")見つからない場合は-1が返ってくるので存在チェックとしても応用できます。
文字列を比較する-containsは既定では大文字・小文字を区別しません。区別したい場合は-ccontainsを使います。文字列の配列に対する[Array]::IndexOf()は大文字・小文字を区別するため、検索方法によって結果が異なることがあります。
配列を並べ替えて重複を削除する
並べ替えはSort-Objectです。
$items = "Banana", "Apple", "Banana", "Orange", "Apple"
$items | Sort-Object降順にしたいときは-Descendingを付けます。重複を除きながら並べ替えたい場合は-Uniqueです。
$items | Sort-Object -Uniqueここで意外と混同しやすいのがSelect-Object -Uniqueとの違いです。Sort-Object -Uniqueは並べ替えと同時に重複を除去しますが、Select-Object -Uniqueは並び順を変えずに重複だけを取り除きます。順番を保ったまま重複だけ消したいのか、ソートしてから重複を消したいのかで使い分けるとよいです。
Select-Object -Uniqueは、既定では大文字・小文字を区別します。「Apple」と「apple」は別の値として残るため、順番だけでなく重複の判定方法にも注意してください。
$items | Select-Object -Unique公式ドキュメントのSelect-Objectにも、-Uniqueが他のパラメーターの後に適用される点が説明されているので-Firstなどと組み合わせるときは一度目を通しておくと安心です。
foreachで配列を順番に処理する
配列を順番に処理する方法は大きく2つあります。foreach文とForEach-Objectコマンドレットです。
foreach ($fruit in $fruits) {
Write-Output $fruit
}$fruits | ForEach-Object { Write-Output $_ }foreachは言語のキーワードで、配列をまるごと受け取ってから順番に処理します。ForEach-Objectはパイプラインで使うコマンドレットで、1件ずつ流れてくるデータをその場で処理していくイメージです。他のコマンドとパイプでつなげたいときや、パイプラインの途中に組み込みたいときはForEach-Objectのほうが書きやすいことが多いです。細かい使い分けまで踏み込むと長くなるので、ここでは基本的な違いだけ押さえておきます。
実測比較で紹介した「foreachの出力をまとめて配列へ格納する」は次の書き方です。ループ内で出力した値をまとめて受け取り@()で配列にします。
$fruits = @("Apple", "Banana", "Orange")
$result = @(foreach ($fruit in $fruits) {
$fruit
})配列と連想配列(ハッシュテーブル)の違い
配列は順番(インデックス)で値を管理しますが、ハッシュテーブルは名前(キー)で値を管理します。
$user = @{
Name = "Taro"
Age = 30
}値を取り出すときはキー名を指定します。
$user.Name
$user["Name"]| 配列 | ハッシュテーブル | |
|---|---|---|
| アクセス方法 | インデックス番号 | キー名 |
| 順序 | 保持される | 保証されない([ordered]を付けると保持可能) |
| 向いている用途 | 同じ種類の値をまとめて扱う | 名前と値をセットで扱う |
ハッシュテーブルの細かい操作は内容が多いので、また別の機会にじっくり扱えればと思います。詳しくは公式のabout_Hash_Tablesも参考にしてください。
配列を使った実践例
実務でよく使う組み合わせを4つ紹介します。以下はWindows環境を想定した例です。C:\workとC:\work\data.csvは、実在するフォルダやCSVファイルのパスへ変更してください。CSVの例ではStatus列を使います。
複数ファイルを順番に処理する
$files = @(Get-ChildItem -Path "C:\work" -File)
foreach ($file in $files) {
Write-Output $file.Name
}特定の拡張子だけ抽出する
$txtFiles = @(
Get-ChildItem -Path "C:\work" -File |
Where-Object { $_.Extension -eq ".txt" }
)サービス一覧から実行中のものだけ抽出する
$runningServices = @(
Get-Service | Where-Object { $_.Status -eq "Running" }
)
$runningServices.CountCSVから取得したデータを条件で絞り込む
$data = @(Import-Csv -Path "C:\work\data.csv")
$filtered = @($data | Where-Object { $_.Status -eq "Active" })絞り込んだデータをCSVへ保存したい場合はExport-Csv の使い方と日本語の文字化けを防ぐ文字コードの指定方法も確認してください。


迷いやすいポイント
まとめ
- 配列は
@()で作れて、複数の値を1つの変数にまとめて管理できるよ。 - 要素の追加は
+=で書けるよ。追加・削除をメソッドで操作したいときはListが便利だよ。 - 削除は基本的に
Where-Objectで除外した新しい配列を作る形になるよ。 - 検索は存在確認なら
-containsや-in、条件で絞り込むならWhere-Objectを使い分けてね。 - ループ処理は
foreachとForEach-Objectで役割が違うから、場面に応じて選んでね。
配列を使うスクリプトの実行方法やファイル操作・ログ監視などの実践例はPowerShellまとめから目的別に探せます。






