シェルのパターンマッチング

パス名展開等で用いるシェルのパターンは正規表現と別物ですが、似た機能を持ちます。

※パターンマッチングは主にパス名展開で用いられますが、**パス名展開以外でも使用可能**な場合があります。
※シェルのパターンマッチングを glob として説明される場合がありますが、基本的には glob はパス名展開におけるパターンマッチングを意味します。
※パターンマッチングの詳細は `man bash` 等を参照。

参考「[3.5.8.1 Pattern Matching - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Pattern-Matching)」

# 1. 基本

|パターン|正規表現|意味|
|-|-|-|
|`*`<br>`**`|`.*`|任意の文字列|
|`?`|`.`|任意の文字|
|`[`...`]`|`[`...`]`|いずれかの文字|
|`[^`...`]`<br>`[!`...`]`|`[^`...`]`|いずれか以外の文字|

※パターン `**` はパス名展開でシェルオプション `globstar` が有効の場合に使用します。

## 1.1. 任意の文字列および文字

```bash:例
[[ 'foo' == *o ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^.*o$ ]] && echo 'OK' || echo 'NG'

[[ 'foo' == ?oo ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^.oo$ ]] && echo 'OK' || echo 'NG'
```

## 1.2. いずれかの文字

```bash:例
[[ 'foo' == [abcdef]oo ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^[abcdef]oo$ ]] && echo 'OK' || echo 'NG'

[[ 'foo' == [a-z]oo ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^[a-z]oo$ ]] && echo 'OK' || echo 'NG'

[[ '-option' == [-+]option ]] && echo 'OK' || echo 'NG'
[[ '-option' =~ ^[-+]option$ ]] && echo 'OK' || echo 'NG'

[[ '[foo]' == [[{]foo[]}] ]] && echo 'OK' || echo 'NG'
[[ '[foo]' =~ ^[[{]foo[]}]$ ]] && echo 'OK' || echo 'NG'

[[ 'foo' == [[:alpha:]]oo ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^[[:alpha:]]oo$ ]] && echo 'OK' || echo 'NG'

[[ 'foo' == [^a-eg-z]oo ]] && echo 'OK' || echo 'NG'
[[ 'foo' == [!a-eg-z]oo ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^[^a-eg-z]oo$ ]] && echo 'OK' || echo 'NG'
```

※一部の記法はパターンマッチングでは使用可能でも**正規表現では使用不可**な場合があります。
※ Bash では仕様上、NULL 文字 `\0` のパターンマッチングを行えません (NULL 文字 `\0` は文字列の終端として扱われるため) 。

記法 `[:`class`:]` を使用することで文字クラスを指定できます。

利用可能な文字クラスは以下の表の通りです。

※「**アルファベット**」には**日本語のひらがな、カタカナおよび漢字等を含みます**。
※日本語等の場合は**記法 `[:alpha:]` は記法 `[:upper:][:lower:]` と等しくなりません**。
※「**スペース**」には**全角スペース等を含みます**。
※ **(要確認)**「**制御文字**」には**ASCII 文字の制御文字 `$'\x01'-$'\x1f\x7f'` 以外を含む可能性があります**。
※ **(要確認)**「数字」は半角のみです。
※**タブは制御文字に含まれる**ため、**記法 `[:graph:]` および記法 ``[:print:]`` はタブとマッチしません**。
※全角スペースはスペースに含まれるため、**記法 `[:graph:]` は記法 `^' '[:cntrl:]` と等しくなりません**。
※全角スペースはスペースに含まれるため、**記法 `[:print:]` は記法 `[:alnum:][:punct:]' '` および記法 `[:graph:]' '` と等しくなりません**。
※「**記号**」には**全角記号を含みます**。
※記法 `[:space:]` はクラス名は `space` となっていますが、実際はスペース以外の全ての空白文字を含みます。
※記法 `[:space:]` の方が記法 `[:blank:]` よりもマッチする文字の種類が多いです。
※ **(要確認)**「アンダースコア」は半角のみです。
※きちんとしたドキュメントでは「`C` ロケールおよび ASCII 文字の場合はこの記法と等しくなる」というような書き方をされます。

|記法|意味|
|-|-|
|`[:alnum:]`|アルファベットと数字<br>`[:alpha:][:digit:]`|
|`[:alpha:]`|アルファベット|
|`[:ascii:]`|ASCII 文字<br>`$'\x01'-$'\x7f'`|
|`[:blank:]`|スペースとタブ|
|`[:cntrl:]`|制御文字|
|`[:digit:]`|数字<br>`0-9`|
|`[:graph:]`|スペースを除く表示可能文字<br>`[:alnum:][:punct:]`|
|`[:lower:]`|小文字アルファベット|
|`[:print:]`|スペースを含む表示可能文字<br>`^[:cntrl:]`|
|`[:punct:]`|記号|
|`[:space:]`|全ての空白文字<br>スペース、タブ、改行、キャリッジリターン、改ページと垂直タブ|
|`[:upper:]`|大文字アルファベット|
|`[:word:]`|アルファベット、数字とアンダースコア|
|`[:xdigit:]`|16 進数の数字<br>`0-9A-Fa-f`|

他に「等価クラス」にマッチする記法 `[=`c`=]` および「照合シンボル」にマッチする記法 `[.`symbol`.]` が存在します。

# 2. 拡張パターン

Bash では拡張パターンマッチングを利用可能です。

条件コマンド `[[ expression ]]` では常に使用可能で、それ以外ではシェルオプション `extglob` を有効にすることで使えるようになります。

※パス名展開だけでなく `case` コマンドおよびパラメータ展開でもシェルオプション `extglob` の有効化が必要です。

|パターン|正規表現|意味|
|-|-|-|
|`@(`pattern-list`)`|`(`pattern-list`)`|グループ|
|`!(`pattern-list`)`|(Bash ではなし)|グループの否定|
|`?(`pattern-list`)`|`(`pattern-list`)?`|0 回または 1 回の繰り返し|
|`*(`pattern-list`)`|`(`pattern-list`)*`|0 回以上の繰り返し|
|`+(`pattern-list`)`|`(`pattern-list`)+`|1 回以上の繰り返し|

※ Bash の正規表現はキャプチャなしグループを扱えません。
※ Bash の正規表現は否定先読み出来ないため、グループを否定したい場合は論理否定を用います。

## 2.1. グループ

```bash:例
[[ 'foo' == @(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo|bar|baz)$ ]] && echo 'OK' || echo 'NG'

[[ 'qux' == !(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ ! 'qux' =~ ^(foo|bar|baz)$ ]] && echo 'OK' || echo 'NG'
```

## 2.2. 0 回または 1 回の繰り返し

※例として分かりやすくするために不等価演算子 `!=` を使用しないようにしています。

```bash:例
[[ '' == ?(foo) ]] && echo 'OK' || echo 'NG'
[[ 'foo' == ?(foo) ]] && echo 'OK' || echo 'NG'
[[ ! 'foofoofoo' == ?(foo) ]] && echo 'OK' || echo 'NG'

[[ '' =~ ^(foo)?$ ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo)?$ ]] && echo 'OK' || echo 'NG'
[[ ! 'foofoofoo' =~ ^(foo)?$ ]] && echo 'OK' || echo 'NG'

[[ '' == ?(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ 'foo' == ?(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ ! 'foobarbaz' == ?(foo|bar|baz) ]] && echo 'OK' || echo 'NG'

[[ '' =~ ^(foo|bar|baz)?$ ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo|bar|baz)?$ ]] && echo 'OK' || echo 'NG'
[[ ! 'foobarbaz' =~ ^(foo|bar|baz)?$ ]] && echo 'OK' || echo 'NG'
```

## 2.3. 0 回以上の繰り返し

```bash:例
[[ '' == *(foo) ]] && echo 'OK' || echo 'NG'
[[ 'foo' == *(foo) ]] && echo 'OK' || echo 'NG'
[[ 'foofoofoo' == *(foo) ]] && echo 'OK' || echo 'NG'

[[ '' =~ ^(foo)*$ ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo)*$ ]] && echo 'OK' || echo 'NG'
[[ 'foofoofoo' =~ ^(foo)*$ ]] && echo 'OK' || echo 'NG'

[[ '' == *(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ 'foo' == *(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ 'foobarbaz' == *(foo|bar|baz) ]] && echo 'OK' || echo 'NG'

[[ '' =~ ^(foo|bar|baz)*$ ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo|bar|baz)*$ ]] && echo 'OK' || echo 'NG'
[[ 'foobarbaz' =~ ^(foo|bar|baz)*$ ]] && echo 'OK' || echo 'NG'
```

## 2.4. 1 回以上の繰り返し

※例として分かりやすくするために不等価演算子 `!=` を使用しないようにしています。

```bash:例
[[ ! '' == +(foo) ]] && echo 'OK' || echo 'NG'
[[ 'foo' == +(foo) ]] && echo 'OK' || echo 'NG'
[[ 'foofoofoo' == +(foo) ]] && echo 'OK' || echo 'NG'

[[ ! '' =~ ^(foo)+$ ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo)+$ ]] && echo 'OK' || echo 'NG'
[[ 'foofoofoo' =~ ^(foo)+$ ]] && echo 'OK' || echo 'NG'

[[ ! '' == +(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ 'foo' == +(foo|bar|baz) ]] && echo 'OK' || echo 'NG'
[[ 'foobarbaz' == +(foo|bar|baz) ]] && echo 'OK' || echo 'NG'

[[ ! '' =~ ^(foo|bar|baz)+$ ]] && echo 'OK' || echo 'NG'
[[ 'foo' =~ ^(foo|bar|baz)+$ ]] && echo 'OK' || echo 'NG'
[[ 'foobarbaz' =~ ^(foo|bar|baz)+$ ]] && echo 'OK' || echo 'NG'
```

# 3. パターンマッチングを使用可能な場所

パターンマッチングは主にパス名展開で用いられますが、パス名展開以外でも使用可能な場合があります。

※パス名展開とパス名展開以外では**オプションが異なる**場合があります。

参考「[Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html)」

## 3.1. パス名展開

`echo` コマンドの引数等でもパス名展開が行われます。

パス名展開は様々な場所で行われますが、以下の場所では**行われません**:

- `case` コマンド
    - パス名展開は行われないがパターンマッチングは使用可能 ※後述
- 条件コマンド `[[ expression ]]`
    - パス名展開は行われないがパターンマッチングは使用可能 ※後述
    - **※ `test expr` および `[ expr ]` ではパス名展開が行われるため注意**
- シングルクォート `'` で囲まれた文字列
    - 展開が行われない
- ダブルクォート `"` で囲まれた文字列
    - 一部の展開は行われるがパス名展開は行われない
- 代入文 `name=[value]`
    - 一部の展開は行われるがパス名展開は行われない
    - **※代入した値を展開する際にパス名展開が行われる場合があるため注意**
- ヒアドキュメント
    - 開始行は展開が行われない
    - 中の行は一部の展開が行われる場合があるがパス名展開は行われない
- ヒアストリング
    - 一部の展開が行われる場合があるがパス名展開は行われない
- POSIX モードで非対話時のリダイレクト演算子の後
    - POSIX モードでも対話時はリダイレクト演算子の後でパス名展開が行われる
    - POSIX モード以外では対話時か非対話時かに関わらずリダイレクト演算子の後でパス名展開が行われる

パス名展開は以下の用途でも用いられます:

- プログラム補完 コマンド
    - `compgen` `-G globpat`
    - `complete` `-G globpat`
- Readline コマンド
    - `glob-complete-word` `M-g`
    - `glob-expand-word` `C-x *`
    - `glob-list-expansions` `C-x g`
- シェル変数
    - `GLOBIGNORE`

参考「[3.5.8 Filename Expansion - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Filename-Expansion)」

## 3.2. パス名展開以外

以下の場所ではパス名展開は行われませんがパターンマッチングを使用可能です:

- `case` コマンド
- 条件コマンド `[[ expression ]]`
    - `test expr` および `[ expr ]` では使用不可
- パラメータ展開
    - パターン削除
        - `${parameter#pattern}`
        - `${parameter##pattern}`
        - `${parameter%pattern}`
        - `${parameter%%pattern}`
    - パターン置換
        - `${parameter/pattern/string}`
        - `${parameter//pattern/string}`
        - `${parameter/#pattern/string}`
        - `${parameter/%pattern/string}`
        - `${parameter/pattern}`
        - `${parameter//pattern}`
        - `${parameter/#pattern}`
        - `${parameter/%pattern}`
    - 大文字小文字変換
        - `${parameter^pattern}`
        - `${parameter^^pattern}`
        - `${parameter,pattern}`
        - `${parameter,,pattern}`

パス名展開以外のパターンマッチングは以下の用途でも用いられます:

- `help` コマンド
- プログラム補完 コマンド
    - `compgen` `-X filterpat`
    - `complete` `-X filterpat`
- シェル変数
    - `EXECIGNORE`
    - `HISTIGNORE`

# 4. オプション

パターンマッチングのオプションに関しては別記事にしました。

参考「[[シェル] パターンマッチングのオプション - Qiita](https://qiita.com/kerupani129/items/2f63ab081392c04ad647)」