【PowerShell】正規表現で文字列を検索・置換する方法|-match・-replace・$Matches・名前付きグループ

【PowerShell】正規表現で文字列を検索・置換する方法|-match・-replace・$Matches・名前付きグループ PowerShell

PowerShellで文字列のパターン処理を行うときの中心が、正規表現の演算子-match(一致の判定と抽出)と-replace(置換)です。ログから特定の行を探す、日付やIDを取り出す、文字列を一括変換するといった処理が、短く書けます。

ただし、PowerShellの正規表現には他の言語と違う特徴がいくつかあります。既定で大文字小文字を区別しないこと、置換文字列はシングルクォートで囲む必要があること、パターンは正規表現なので特殊文字をエスケープすることです。これらを知らないと、思った結果になりません。この記事では、実機で確認しながら確実な書き方を解説します。

先に結論

  • "文字列" -match "パターン"は一致すれば$trueを返し、結果を$Matchesに入れます。
  • 取り出しは$Matches[0](全体)、$Matches[1](グループ)、名前付きなら$Matches["名前"]です。
  • 既定で大文字小文字を区別しません。区別するには-cmatch-creplaceを使います。
  • 置換は"文字列" -replace "パターン", "置換後"。後方参照は$1です。
  • 置換文字列はシングルクォートで囲みます。ダブルクォートだと$1が変数展開で消えます。
  • パターンは正規表現です。.(などの特殊文字は\.のようにエスケープします。

正規表現は、データの抽出やログ監視で多用します。条件一致したデータだけを抽出する方法ログの特定キーワードを監視する方法本文から特定の情報を抽出する方法とあわせて使うと効果的です。

スポンサーリンク

-matchで一致するか調べる

-matchは、左の文字列が右のパターンに一致するかを$true$falseで返します。同時に、一致した内容が自動変数$Matchesに格納されます。

match-basic.ps1
# 一致すれば $true、同時に $Matches が埋まる
"order-12345" -match "\d+"   # True

$Matches[0]   # 12345(一致した部分全体)

# 判定だけに使うことも多い(if と組み合わせ)
if ("user@example.com" -match "@") {
    Write-Host "メールアドレスっぽい"
}

# 否定は -notmatch
"abc" -notmatch "\d"   # True(数字を含まない)

実機でも、"order-12345" -match "\d+"Trueを返し、$Matches[0]12345になりました。-matchは判定と抽出を同時に行うのが特徴です。

$Matchesでグループを取り出す

パターンの一部を丸かっこ( )で囲むと「グループ」になり、その部分を個別に取り出せます。$Matches[1]が1番目のグループ、$Matches[2]が2番目です。

matches-groups.ps1
if ("2026-03-09" -match "(\d{4})-(\d{2})-(\d{2})") {
    $Matches[0]   # 2026-03-09(全体)
    $Matches[1]   # 2026(年)
    $Matches[2]   # 03(月)
    $Matches[3]   # 09(日)
}

実機でも、$Matches[1]2026$Matches[2]03$Matches[3]09になりました。番号は左のかっこから順に1, 2, 3と振られます。

名前付きグループで分かりやすく取り出す

グループが増えると番号での管理は分かりにくくなります。(?<名前>...)の形で名前を付けると、$Matches["名前"]で取り出せて読みやすくなります。

named-groups.ps1
if ("2026-03-09" -match "(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})") {
    $Matches["year"]    # 2026
    $Matches.month      # 03(ドットでもアクセスできる)
    $Matches["day"]     # 09
}
名前付きグループでは番号アクセスが使えない

実機で確認したところ、(?<year>...)のように名前付きグループを使うと、$Matches["year"]では取り出せる一方、$Matches[1]は空でした。名前を付けたグループは、番号ではなく名前でアクセスしてください。番号で取り出したい場合は名前を付けずに( )だけにします。

【重要】既定で大文字小文字を区別しない

PowerShellの-match-replaceは、既定で大文字小文字を区別しません。多くのプログラミング言語と逆なので、特に注意が必要です。区別したいときは、頭にcを付けた-cmatch-creplaceを使います。

case-insensitive.ps1
# 既定は大文字小文字を区別しない
"ABC" -match "abc"    # True(一致する)

# -cmatch なら区別する(c = case-sensitive)
"ABC" -cmatch "abc"   # False(一致しない)

# 置換も同様。-creplace で区別する
"Hello HELLO" -replace "hello", "Hi"     # Hi Hi(両方置換)
"Hello HELLO" -creplace "hello", "Hi"    # Hello HELLO(小文字のみ対象、一致なし)

実機でも、"ABC" -match "abc"True-cmatchではFalseになりました。「区別しない」のが既定だと意識しておかないと、意図せず別のものまで一致してしまいます。

-replaceで置換する(後方参照とシングルクォート)

置換は"文字列" -replace "パターン", "置換後"です。置換後の文字列で$1$2と書くと、一致したグループを差し込めます(後方参照)。名前付きグループは${名前}で参照します。

replace-basic.ps1
# 単純な置換
"a1b2c3" -replace "\d", "X"   # aXbXcX(数字をすべて X に)

# 後方参照で日付の並びを入れ替える
"2026-03-09" -replace "(\d{4})-(\d{2})-(\d{2})", '$3/$2/$1'
# 09/03/2026

# 名前付きグループは ${名前}
"2026-03-09" -replace "(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})", '${y}年${m}月${d}日'
# 2026年03月09日
置換文字列はシングルクォートで囲む

後方参照を使うときは、置換文字列を必ずシングルクォートで囲んでください。ダブルクォートにすると、-replaceが処理する前にPowerShellが$1を変数として展開し、空文字になってしまいます。実機でも、"$1"(ダブルクォート)では結果が空になり、'$3/$2/$1'(シングルクォート)では正しく09/03/2026になりました。なお、置換後に$そのものを出したいときは$$と書きます。

【重要】パターンは正規表現(特殊文字をエスケープ)

-replaceでつまずきやすいのが、左のパターンが正規表現として解釈されることです。.(任意の1文字)、*([などは特殊な意味を持つため、文字そのものとして扱いたいときはエスケープが必要です。

replace-escape.ps1
# NG: . は「任意の1文字」なので全部が置換される
"a.b.c" -replace ".", "-"    # ----- (5文字すべて)

# OK: \. でドットそのものを指定する
"a.b.c" -replace "\.", "-"   # a-b-c

# 変数など内容が不定なら [regex]::Escape で安全にエスケープ
$keyword = "a.b"
$escaped = [regex]::Escape($keyword)   # a\.b
"xa.by" -replace $escaped, "_"          # x_y

実機でも、"a.b.c" -replace ".", "-"-----(すべて置換)、"\."にするとa-b-cになりました。利用者の入力や変数を検索文字列に使う場合は、[regex]::Escape()で特殊文字をまとめてエスケープしておくと安全です。単純な文字列置換で正規表現が不要なら、.Replace()メソッド(こちらは正規表現ではない)を使う手もあります。

配列に対する-matchはフィルタになる

-matchの左に配列を置くと、戻り値は$true$falseではなく、パターンに一致した要素だけの配列になります。Where-Objectを使わずに絞り込めます。

array-match-filter.ps1
$files = @("apple1", "banana", "cherry7")

# 数字を含む要素だけが返る(フィルタ)
$files -match "\d"          # apple1, cherry7

# 否定で「含まないもの」を絞る
$files -notmatch "\d"       # banana

# 置換も配列をまとめて処理できる
@("img1.png", "img2.png") -replace "\.png", ".webp"
# img1.webp, img2.webp
配列のときは $Matches が埋まらない

配列に対する-matchは「一致した要素を返すフィルタ」として働くため、$Matchesには一致部分が入りません。$Matchesでグループを取り出したいときは、foreachで1件ずつ-matchするか、後述の[regex]クラスを使います。実機でも、配列-match "\d"apple1, cherry7を返しました。

Select-Stringでファイルから検索する(grep相当)

ファイルの中身を正規表現で検索するにはSelect-Stringを使います。Linuxのgrepに相当し、一致した行を行番号付きで返します。

select-string.ps1
# ファイルから "error" を含む行を探す(既定で大文字小文字を区別しない)
Select-String -Path "C:\logs\app.log" -Pattern "error"

# 複数ファイルをまとめて検索
Select-String -Path "C:\logs\*.log" -Pattern "timeout|failed"

# 結果から行番号と内容を取り出す
$hits = Select-String -Path "C:\logs\app.log" -Pattern "error"
foreach ($hit in $hits) {
    Write-Host "$($hit.LineNumber): $($hit.Line)"
}

# 大文字小文字を区別するなら -CaseSensitive
Select-String -Path "C:\logs\app.log" -Pattern "ERROR" -CaseSensitive

実機でも、errorERRORの両方が既定のSelect-Stringで一致し、行番号13として取得できました。Select-String-matchと同じく既定で大文字小文字を区別しないため、区別したい場合は-CaseSensitiveを付けます。ログ監視と組み合わせる方法はログの特定キーワードを監視する方法で解説しています。

[regex]クラスで全件取得する

-matchは最初の一致しか$Matchesに入れません。1つの文字列の中のすべての一致を取り出したいときは、.NETの[regex]クラスを使います。

regex-matches.ps1
# 文字列内のすべての数値を取り出す
$all = [regex]::Matches("a1 b22 c333", "\d+")

foreach ($m in $all) {
    $m.Value   # 1, 22, 333 が順に取れる
}

# 値だけを配列にする
($all | ForEach-Object { $_.Value })   # 1, 22, 333

# 動的な置換(一致ごとに処理を変える)
[regex]::Replace("a1 b2", "\d", { param($m) [int]$m.Value * 10 })
# a10 b20

実機でも、[regex]::Matches("a1 b22 c333", "\d+")1, 22, 333をすべて返しました。[regex]::Replace()にスクリプトブロックを渡すと、一致ごとに計算して置換するような高度な処理もできます。

よくある失敗

大文字小文字が区別されると思い込む

-match-replaceSelect-Stringはいずれも既定で区別しません。区別したいなら-cmatch-creplace-CaseSensitiveを使います。

置換文字列をダブルクォートで囲んでが消える

ダブルクォートだとPowerShellが$1を変数展開します。後方参照を使う置換文字列は、必ずシングルクォートで囲んでください。

パターンの特殊文字をエスケープし忘れる

.(などは正規表現の特殊文字です。文字そのものを指定するなら\.のようにエスケープするか、[regex]::Escape()を使います。

名前付きグループを番号で取り出そうとする

名前を付けたグループは$Matches["名前"]で取り出します。$Matches[1]では取れません。番号で取りたいなら名前を付けません。

配列の-matchで$Matchesを見て混乱する

配列に対する-matchはフィルタで、$Matchesは埋まりません。グループを取り出すなら1件ずつ処理するか[regex]クラスを使います。

よくある質問

Qなぜ大文字小文字が区別されないのですか?
APowerShellの比較・一致演算子は、既定で大文字小文字を区別しない仕様だからです。区別したい場合は、-match-cmatch-replace-creplaceSelect-String-CaseSensitiveを使ってください。
Q置換文字列はなぜシングルクォートなのですか?
Aダブルクォートだと、-replaceが処理する前にPowerShellが$1などを変数として展開し、空文字になってしまうためです。後方参照を使うときはシングルクォートで囲み、$1をそのまま-replaceへ渡します。
Q一致部分をすべて取り出すには?
A-matchは最初の1件だけです。すべての一致は[regex]::Matches(文字列, パターン)で取得し、各要素の.Valueで値を取り出します。
Qファイルの中身を正規表現で検索したいです。
ASelect-String -Path ファイル -Pattern パターンを使います。一致した行を行番号付きで返し、Linuxのgrepのように使えます。複数ファイルやワイルドカードにも対応します。
Q正規表現ではなく単純な文字列置換をしたいです。
A特殊文字を気にせず単純に置き換えたいなら、文字列の.Replace("古い", "新しい")メソッドが使えます。これは正規表現ではないため、.などもそのままの文字として扱われます。

まとめ

  • -matchは一致判定と抽出を同時に行い、結果は$Matchesに入ります。
  • グループは$Matches[1]、名前付きは$Matches["名前"]で取り出します。
  • 既定で大文字小文字を区別しません。区別するなら-cmatch-creplaceです。
  • -replaceの後方参照は$1。置換文字列はシングルクォートで囲みます。
  • パターンは正規表現なので、特殊文字は\.[regex]::Escape()でエスケープします。
  • ファイル検索はSelect-String、全件取得は[regex]::Matchesを使います。

PowerShellの正規表現は、「大文字小文字を区別しない」「置換文字列はシングルクォート」「パターンは正規表現」という3つの特徴さえ押さえれば、検索も置換も思いどおりに書けます。ログ解析やデータ抽出のスクリプトが、ぐっと短く正確になります。