【PowerShell】正規表現の利用

# はじめに

職場でPowerShellのコードを見ていたら正規表現を使った処理が幾つも出てきたのですが、その一部にあまり見慣れない書き方があったので記事としてまとめてみました。


# 学習環境

今回はVS Codeを使いました。

# 正規表現の基礎

* PowerShellには`match`と`like`、`notmatch`と`notlike`という似た意味合いの演算子がありますが、それぞれ役割が違っています。

|演算子名|意味|
|---|---|
|-match|文字列が正規表現パターンと一致|
|-notmatch|文字列が正規表現パターンと不一致|
|-like|文字列がワイルドカード（*）を含むパターンと一致|
|-notlike|文字列がワイルドカード（*）を含むパターンと不一致|

```powershell:match/notmatch/like/notlikeの使い分け
$text = "2023-12-05"

Write-Host "match: $($text -match "^\d{4}-\d{2}-\d{2}$")"
Write-Host "notmatch: $($text -notmatch "^\d{4}-\d{2}-\d{2}$")"

Write-Host "like: $($text -like "2023-11*")"
Write-Host "notlike: $($text -notlike "2023-11*")"
```

```text:実行結果
match: True
notmatch: False
like: False
notlike: True
```

# グループ化コンストラクトとキャプチャ

* [Microsoft Learn](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.4#groups-captures-and-substitutions)を見ると、キャプチャというものについて以下のように書かれていました。
 
> グループ化コンストラクトは、キャプチャまたは無視できる部分文字列に入力文字列を分離します。 
> グループ化された部分文字列は、部分式と呼ばれます。 
> 既定では、部分式は番号付きグループにキャプチャされますが、名前を割り当てることもできます。

* はっきりと説明が書かれていないので推測ですが、「グループ化コンストラクト」と「グループ化された部分文字列」「キャプチャ」はそれぞれ以下のような意味合いだと思われます。

|用語|意味（推測ですが...）|
|---|---|
|グループ化コンストラクト|正規表現中で半角丸カッコで囲まれた部分のこと。|
|グループ化された部分文字列|「グループ化コンストラクト」にマッチした部分文字列のこと。|
|キャプチャ|マッチした文字列を捕捉すること。|

* またキャプチャされたテキストについては、以下のようにHashtable型の`$Matches`という変数に格納されるそうです。

> キャプチャされたテキストを取得するには、 `$Matches`Hashtable 自動変数を使用します。 一致全体を表すテキストは、キー 0に格納されます。

* 以下の「キャプチャの例（1）」を見ると、確かに`$Matches[0]`に「マッチした部分の全体」が入っていました。

```powershell:キャプチャの例（1）
$text = "created_date: 2023-12-05 22:18:25"
$text -match "\d{4}-\d{2}-\d{2}"
$Matches
```

```text:実行結果（1）
True

Name                           Value
----                           -----
0                              2023-12-05
```

* 次に「グループ化コンストラクト」を使ってみると、`$Matches[1]`以降にグループ化コンストラクトにマッチした部分が順に入っていました。
  * この書き方だと、Split関数を使ってゴリゴリ書くよりもスマートに感じます。
  * 一方で、PowerShellに詳しくない人が`$Matches[1]`を見ても「年」を取り出しているとは想像しづらいと思うので、適切なコメントが必要なのではと感じました。

```powershell:キャプチャの例（2）
$text = "created_date: 2023-12-05 22:18:25"
$text -match "(\d{4})-(\d{2})-(\d{2})"
$Matches
```

```text:実行結果（2）
True

3                              05
2                              12
1                              2023
0                              2023-12-05
```

# 名前付きキャプチャ

* さらに、グループ化コンストラクトの先頭に`?<keyname>`を付けることで、`$Matches`から番号（index）ではなく名称を指定してマッチした文字列を取得することができます。
  * これはとても便利な機能である反面、正規表現が長くなって読みづらくなると思うので、使いどころを選ぶように感じます...

```powershell:キャプチャの例（3）
$text = "created_date: 2023-12-05 22:18:25"
$text -match "(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})"
$Matches
```


```text:実行結果（3）
True

Name                           Value
----                           -----
month                          12
day                            05
year                           2023
0                              2023-12-05
```


# 参考URL

* [about_Regular_Expressions - Microsoft Learn](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.4)
* [一致する演算子(about_Comparison_Operators) - Microsoft Learn](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-7.4#matching-operators)