IPv4 プライベートアドレスにマッチする正規表現

# 解説不要な方向け
```
/^10\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$|^172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$|^192\.168\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$/
```

マッチさせたい文字列の前後に任意の文字が含まれていてもマッチさせたい場合は以下のように `^` と `$` を除きます。

```
/10\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|192\.168\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/
```

※ 解説不要な場合はここから下は見なくても大丈夫です。

# はじめに
IPv4 のプライベート IP アドレスにマッチする正規表現の書き方を紹介します。なお、ここで紹介するのは 10 進数です。

# プライベート IP アドレスの範囲
IPv4 のプライベート IP アドレスの範囲は以下の 3 つです。

| プレフィックス表記 | 人間にわかりやすい表記 |
|---|---|
| `10.0.0.0/8` | `10.0.0.0` 〜 `10.255.255.255` |
| `172.16.0.0/12` | `172.16.0.0`〜 `172.31.255.255` |
| `192.168.0.0/16` | `192.168.0.0`〜 `192.168.255.255` |

[プライベートIPアドレスの範囲](http://itdoc.hitachi.co.jp/manuals/3021/3021324220/NNMS0200.HTM)

上記の範囲内に含まれる IPv4 アドレスのみにマッチする正規表現です。

# 正規表現
上記のプライベート IP アドレスの範囲にのみマッチする正規表現は以下の通りです。

```
/^10\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$|^172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$|^192\.168\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$/
```

マッチさせたい文字列の前後に任意の文字が含まれていてもマッチさせたい場合は以下のように `^` と `$` を除きます。

```
/10\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|192\.168\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])/
```

## 解説
上記の正規表現を分割すると以下の 3 つになります。

```:10.0.0.0/8に該当
^10\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$
```

```:172.16.0.0/12に該当
^172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$
```

```:192.168.0.0/16に該当
^192\.168\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$
```

それぞれ、`10.0.0.0/8`、`172.16.0.0/12`、`192.168.0.0/16` の正規表現に該当します。それらを互いに論理和 (`|`) でつなげています。

### 0 〜 255 の範囲の表現
`([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])` の部分が 0 〜 255 の数値の範囲を指定しているのですが、もう少し細かく説明すると、以下の 5 つを論理和でつなげています。

- 0 〜 9 の範囲
- 10 〜 99 の範囲
- 100 〜 199 の範囲
- 200 〜 249 の範囲
- 250 〜 255 の範囲

このように分けて考えないと、たとえば `001` とか `259` とかにもマッチしてしまうことになります。

また、前後の `()` はグルーピングです。上記 5 つの範囲指定をひとまとめとして扱うためです。

### 16 〜 31 の範囲の表現
`(1[6-9]|2[0-9]|3[0-1])` は基本的に 0 〜 255 の範囲の表現と同じ書き方です。以下の 3 つを論理和でつなげています。

- 16 〜 19 の範囲
- 20 〜 29 の範囲
- 30 〜 31 の範囲 (30 と 31)

### ドットの表現
間のドットは `\.` です。`.` は正規表現の記号の一つなので、`.` という記号自体を表現したい場合は `\` でエスケープして `\.` と表記します。

## 使用例
`ip` コマンドや `ifconfig` コマンドなどでプライベート IP アドレスが設定されているネットワークインターフェース名を調べるときなどに便利です。

```shell
$ (ip a 2>/dev/null || ifconfig 2>/dev/null) | grep -E '10\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|172\.(1[6-9]|2[0-9]|3[0-1])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])|192\.168\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' -B 10
```

Linux では `ip` コマンド、macOS では `ifconfig` コマンドを使うので `(ip a 2>/dev/null || ifconfig 2>/dev/null)` としています[^1]。

[^1]: `ip` コマンドがあれば (コマンドの実行が失敗しなければ) `ip` コマンドを実行し、`ip` コマンドがなくても `ifconfig` コマンドがあれば `ifconfig` コマンドを実行します。両方ある場合は `ip` コマンド (先頭に書いたほうのコマンド) が実行されます。

grep の場合は `//` の代わりに `''` で囲みます。また、表示される結果は、プライベート IP アドレスの前後に `inet` や `brd` などの文字列が入るため、`^` と `$` は除いています。

`-E` は正規表現を使うオプションです。`-B 10` はマッチした行とその上の 10 行も一緒に表示するオプションです。

結果はこんな感じになります。

```
... (省略)
...
...
...
...
...
...
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.2/24 brd 192.168.3.255 scope global eth0
```

`inet 192.168.3.2/24 brd 192.168.3.255 scope global eth0` の行にマッチして、さらにその上の 10 行も表示されています。

ネットワークインターフェース名が `eth0` であることがわかります。