[シェル] パターンマッチングのオプション

パターンマッチングに関するオプションが存在しますが、パス名展開かそれ以外かで異なる場合があります。

例えば、シェルオプション `nocaseglob` はパス名展開用で、シェルオプション `nocasematch` はパス名展開以外用です。

参考「[Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html)」
参考「[4.3.1 The Set Builtin - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin)」
参考「[4.3.2 The Shopt Builtin - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#The-Shopt-Builtin)」
参考「[5.2 Bash Variables - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Bash-Variables)」

パターンマッチングに関しては別記事にしました。

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

# 0. まとめ

シェルオプション `extglob` および `globasciiranges` を除いて、名前に `glob` を含むオプションはパス名展開用です。

※**互換モードのバージョン**によって一部の挙動が変化する場合があります。
※パス名展開用のオプションを変更してもパス名展開以外には影響しません。
※パス名展開以外用のオプションを変更してもパス名展開には影響しません。

共通:

- シェルオプション
    - `extglob`
    - `globasciiranges`
- シェル変数
    - `LC_ALL`
    - `LC_COLLATE`
    - `LC_CTYPE`

パス名展開:

- オプション
    - `-f`
    - `-o noglob`
- シェルオプション
    - `dotglob`
    - `globskipdots`
    - `failglob`
    - `nullglob`
    - `globstar`
    - `nocaseglob`
- シェル変数
    - `GLOBIGNORE`

パス名展開以外:

- シェルオプション
    - `nocasematch`
    - `patsub_replacement`

# 1. 共通

## 1.1. シェルオプション

|シェルオプション|意味|
|-|-|
|`extglob`|拡張パターンマッチングを有効化する|
|`globasciiranges`|パターン `[`...`]` の範囲表現を `C` ロケール扱いにする|

※上記のシェルオプションは正規表現には影響しません。

### 1.1.1. 拡張パターンマッチング

シェルオプション `extglob` を有効化することで拡張パターンマッチングを利用可能になります。

シェルオプション `extglob` の影響を受ける場所は以下の通りです:

- パス名展開
- `case` コマンド
- パラメータ展開
    - パターン削除
        - `${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}`

※ **条件コマンド `[[ expression ]]` では常に拡張パターンマッチングを使用可能**なため、シェルオプション `extglob` の影響を受けません。

```bash
shopt -s extglob

# 
function f() {

	echo /usr/@(lib)

	case 'foo' in
		@(foo)) echo 'OK' ;;
		*) echo 'NG' ;;
	esac

	local -r parameter='foo bar'
	echo "${parameter/@(foo)/baz}"
	echo "${parameter#@(foo) }"
	echo "${parameter^@(f)}"

}

f
```

```text:実行結果
/usr/lib
OK
baz bar
bar
Foo bar
```

### 1.1.2. ASCII 範囲

パターン `[`...`]` の範囲表現はロケールによって動作が変化しますが、シェルオプション `globasciiranges` を用いることで `C` ロケール扱いに出来ます。

※シェル変数の値の設定に `export` は不要ですが、ここでは `sort` コマンドにも設定を反映させるために `export` しています。

```bash
export LC_COLLATE=en_US.UTF-8
echo -e 'A\nB\na\nb\n' | sort | xargs

shopt -s globasciiranges
echo /usr/li[A-B]
[[ 'b' == [A-B] ]] && echo 'OK' || echo 'NG'

shopt -u globasciiranges
echo /usr/li[A-B]
[[ 'b' == [A-B] ]] && echo 'OK' || echo 'NG'
```

```text:実行結果
a A b B
/usr/li[A-B]
NG
/usr/lib
OK
```

## 1.2. シェル変数

|シェル変数|意味|
|-|-|
|`LC_ALL`|`LANG` および他の `LC_` 変数の値を上書きする|
|`LC_COLLATE`|パターン `[`...`]` の範囲表現、等価クラス `[=`c`=]`、照合シンボル `[.`symbol`.]` および照合順序の動作を決定する|
|`LC_CTYPE`|文字の解釈と文字クラス `[:`class`:]` の動作を決定する|

※コマンド実行時に直接 1 行で `NAME=value command arguments` のようにシェル変数の値を変更した場合、`command` の引数の `arguments` に含まれるパターンに**設定が反映されない**ため注意。
※シェル変数の値の設定に `export` は**不要**です。
※上記のシェル変数は正規表現の一部にも影響します。

### 1.2.1. 照合

※シェル変数の値の設定に `export` は不要ですが、ここでは `sort` コマンドにも設定を反映させるために `export` しています。

```bash
shopt -u globasciiranges

export LC_COLLATE=C.UTF-8
echo -e 'A\nB\na\nb\n' | sort | xargs
echo /usr/li[A-B]
[[ 'b' == [A-B] ]] && echo 'OK' || echo 'NG'

export LC_COLLATE=en_US.UTF-8
echo -e 'A\nB\na\nb\n' | sort | xargs
echo /usr/li[A-B]
[[ 'b' == [A-B] ]] && echo 'OK' || echo 'NG'
```

```text:実行結果
A B a b
/usr/li[A-B]
NG
a A b B
/usr/lib
OK
```

### 1.2.2. 文字タイプ

```bash
touch あ

LC_CTYPE=C.UTF-8
[[ 'あ' == [[:alpha:]] ]] && echo 'OK' || echo 'NG'
echo [[:alpha:]]

LC_CTYPE=C
[[ 'あ' == [[:alpha:]] ]] && echo 'OK' || echo 'NG'
echo [[:alpha:]]
```

```text:実行結果
OK
あ
NG
[[:alpha:]]
```

# 2. パス名展開

## 2.1. オプション

|オプション|意味|
|-|-|
|`-f`<br>`-o noglob`|パス名展開を無効化する|

※上記のオプションはパス名展開以外には影響しません。
※上記のオプションは正規表現には影響しません。

### 2.1.1. 無効化

```bash
# 
set +f
echo /usr/li?

set -f
echo /usr/li?

# 
set +o noglob
echo /usr/li?

set -o noglob
echo /usr/li?
```

```text:実行結果
/usr/lib
/usr/li?
/usr/lib
/usr/li?
```

## 2.2. シェルオプション

|シェルオプション|意味|
|-|-|
|`dotglob`|ファイル名 `.` および `..` を除いて、`.` で始まるファイル名と暗黙的に一致するようにする|
|`globskipdots`|ファイル名 `.` および `..` はパターンと一致しないようにする|
|`failglob`|パターンと一致しない場合はエラーにする|
|`nullglob`|パターンと一致しない場合は空文字列にする|
|`globstar`|パターン `**` を有効化する|
|`nocaseglob`|大文字小文字を区別しない|

※後述のシェル変数 `GLOBIGNORE` はシェルオプション `dotglob` に影響します。
※後述のシェル変数 `GLOBIGNORE` はシェルオプション `globskipdots` に影響します。
※上記のシェルオプションはパス名展開以外には影響しません。
※上記のシェルオプションは正規表現には影響しません。

### 2.2.1. `.` で始まるファイル名

ファイル名 `.` および `..` を除いて、`.` で始まるファイル名を扱う場合は以下のことに注意が必要です:

- シェルオプション `dotglob` が**無効**の場合、 `.` で始まるパターンと**明示的に一致する**
    - `?` や `[.]` 等で始まるパターンと**暗黙的に一致しない**
- シェルオプション `dotglob` が**有効**の場合、パターンと**暗黙的に一致する**
    - `?` や `[.]` 等で始まるパターンとも**暗黙的に一致する**

ファイル名 `.` および `..` を扱う場合は以下のことに注意が必要です:

- シェルオプション `globskipdots` が**有効**の場合、パターンと**常に一致しない**
    - `.` で始まるパターンとも**明示的に一致しない**
- シェルオプション `globskipdots` が**無効**の場合、`.` で始まるパターンと**明示的に一致する**
    - シェルオプション `dotglob` が有効でも、`?` や `[.]` 等で始まるパターンと**暗黙的に一致しない**

```bash
# 
shopt -u dotglob
echo ~/.ss? | basename "$(cat)"
echo ~/?ssh | basename "$(cat)"

shopt -s dotglob
echo ~/.ss? | basename "$(cat)"
echo ~/?ssh | basename "$(cat)"

# 
shopt -s globskipdots
echo /.[.] | basename "$(cat)"
echo /[.]. | basename "$(cat)"

shopt -u globskipdots
echo /.[.] | basename "$(cat)"
echo /[.]. | basename "$(cat)"
```

```text:実行結果
.ssh
?ssh
.ssh
.ssh
.[.]
[.].
..
[.].
```



### 2.2.2. 不一致の扱い

```bash
shopt -u failglob
(echo /usr/?) 2>/dev/null || echo 'NG'

shopt -s failglob
(echo /usr/?) 2>/dev/null || echo 'NG'
```

```text:実行結果
/usr/?
NG
```

```bash
shopt -u failglob

shopt -u nullglob
echo /usr/?

shopt -s nullglob
echo /usr/?
```

```text:実行結果
/usr/?

```

### 2.2.3. パターン `**`

```bash
mkdir -p directory
touch foo directory/foo

# 
shopt -u globstar
echo **/foo

shopt -s globstar
echo **/foo
```

```text:実行結果
directory/foo
directory/foo foo
```

### 2.2.4. 大文字小文字の区別

```bash
shopt -u nocaseglob
echo /usr/LI?

shopt -s nocaseglob
echo /usr/LI?
```

```text:実行結果
/usr/LI?
/usr/lib
```

## 2.3. シェル変数

|シェル変数|意味|
|-|-|
|`GLOBIGNORE`|無視するファイル名の一覧|

※コマンド実行時に直接 1 行で `NAME=value command arguments` のようにシェル変数の値を変更した場合、`command` の引数の `arguments` に含まれるパターンに**設定が反映されない**ため注意。
※シェル変数の値の設定に `export` は**不要**です。
※シェル変数 `GLOBIGNORE` は前述のシェルオプション `dotglob` に影響します。
※シェル変数 `GLOBIGNORE` は前述のシェルオプション `globskipdots` に影響します。
※上記のシェル変数はパス名展開以外には影響しません。
※上記のシェル変数は正規表現には影響しません。

### 2.3.1. 無視するファイル名

#### 2.3.1.1. 基本

シェル変数 `GLOBIGNORE` で無視するファイル名を指定出来ます。

コロン `:` 区切りで複数のパターンを設定可能です。

※シェル変数 `GLOBIGNORE` に**相対パス**を設定した場合、**カレントディレクトリによって動作が異なる**ため注意。
※シェル変数 `GLOBIGNORE` に**空の値**を設定した場合、**設定していない扱い**になります。

```bash
GLOBIGNORE=/usr/lib*
echo /usr/li?

unset GLOBIGNORE
echo /usr/li?
```

```text:実行結果
/usr/li?
/usr/lib
```

#### 2.3.1.2. ファイル名 `.`、`..` および `.` で始まるファイル名

ファイル名 `.`、`..` および `.` で始まるファイル名に関しては以下のことに注意が必要です:

- ファイル名 `.` および `..` は**必ず無視される**
    - 前述のシェルオプション `globskipdots` が無効でも**必ず無視される**
- 前述のシェルオプション `dotglob` が**有効**になり、`.` で始まるファイル名は**無視されなくなる**
    - `.` で始まるファイル名を無視したい場合はシェル変数 `GLOBIGNORE` にパターン `.*` を追加する
        - ※シェル変数 `GLOBIGNORE` にパターン `.*` を追加した場合、`.` で始まるファイル名と**明示的に一致しなくなる**ため注意
        - ※シェル変数 `GLOBIGNORE` に**相対パス**を設定した場合、**カレントディレクトリによって動作が異なる**ため注意

```bash
shopt -u globskipdots

# 
GLOBIGNORE=/usr/lib*
echo /usr/li?
echo ~/?ssh | basename "$(cat)"
echo /.[.] | basename "$(cat)"

unset GLOBIGNORE
echo /usr/li?
echo ~/?ssh | basename "$(cat)"
echo /.[.] | basename "$(cat)"
```

```text:実行結果
/usr/li?
.ssh
.[.]
/usr/lib
?ssh
..
```

#### 2.3.1.3. シェルオプション `dotglob` に対する影響

シェル変数 `GLOBIGNORE` は前述のシェルオプション `dotglob` に影響する場合があります:

- シェル変数 `GLOBIGNORE` に**空でない値**が設定されると、前述のシェルオプション `dotglob` が**有効になる**
- シェル変数 `GLOBIGNORE` が **`unset`** されると、前述のシェルオプション `dotglob` が**無効になる**
- シェル変数 `GLOBIGNORE` に**空の値**を設定しても、前述のシェルオプション `dotglob` は**有効にも無効にも変更されない**

```bash
shopt -u dotglob
shopt dotglob || :
GLOBIGNORE=/usr/lib*
shopt dotglob || :

shopt -s dotglob
shopt dotglob || :
unset GLOBIGNORE
shopt dotglob || :
```

```text:実行結果
dotglob         off
dotglob         on
dotglob         on
dotglob         off
```

#### 2.3.1.4. 他のオプションから受ける影響

シェル変数 `GLOBIGNORE` は前述のシェルオプションの一部の影響を受けます。

|シェルオプション|あり|なし|
|-|-|-|
|`extglob`|✓||
|`globasciiranges`|✓||
|`dotglob` ※||✓|
|`globskipdots` ※||✓|
|`failglob`||✓|
|`nullglob`||✓|
|`globstar`||✓|
|`nocaseglob`|✓||

※シェルオプション `globstar` が有効でも、シェル変数 `GLOBIGNORE` の値で**パターン `**` は特別な意味を持たず、パターン `*` と同じ意味になる**ため注意。
※シェル変数 `GLOBIGNORE` はシェルオプション `dotglob` および `globskipdots` に影響しますが、シェルオプション `dotglob` および `globskipdots` はシェル変数 `GLOBIGNORE` に影響しません。
※シェル変数 `GLOBIGNORE` に空でない値を設定後に手動でシェルオプション `dotglob` を無効化した場合も、シェル変数 `GLOBIGNORE` の値は `.` で始まるファイル名と暗黙的に一致します。

前述の `LC_` 変数はシェル変数 `GLOBIGNORE` に影響します。

#### 2.3.1.5.  `GLOBIGNORE=.*` と `shopt -u dotglob` の違い

`GLOBIGNORE=.*` および `shopt -u dotglob` はどちらも `.` で始まるファイル名をパス名展開で無視するための設定ですが、以下のような違いがあります:

- `GLOBIGNORE=.*`
    - `.` で始まるファイル名はパターンと**明示的に一致しない**
    - **カレントディレクトリ直下のファイルのみ**に影響する
- `shopt -u dotglob`
    - `.` で始まるファイル名はパターンと**暗黙的に一致しない**
    - `.` で始まるファイル名はパターンと**明示的に一致する**
    - **カレントディレクトリ直下以外のファイルにも**影響する

# 3. パス名展開以外

## 3.1. シェルオプション

|シェルオプション|意味|
|-|-|
|`nocasematch`|	大文字小文字を区別しない|
|`patsub_replacement`|パターン置換 `${parameter/pattern/string}` の `string` 内の `&` を有効化する|

※上記のシェルオプションはパス名展開には影響しません。
※シェルオプション `nocasematch` は正規表現にも影響します。
※シェルオプション `patsub_replacement` は正規表現には影響しません。

### 3.1.1. 大文字小文字の区別

シェルオプション `nocasematch` を有効化することで大文字小文字を区別しなくなります。

シェルオプション `nocasematch` の影響を受ける場所は以下の通りです:

- `case` コマンド
- 条件コマンド `[[ expression ]]`
- パラメータ展開
    - パターン置換
        - `${parameter/pattern/string}`
        - `${parameter//pattern/string}`
        - `${parameter/#pattern/string}`
        - `${parameter/%pattern/string}`
        - `${parameter/pattern}`
        - `${parameter//pattern}`
        - `${parameter/#pattern}`
        - `${parameter/%pattern}`
- プログラム補完 コマンド
    - `compgen` `-X filterpat`
    - `complete` `-X filterpat`

```bash
# 
function f() {

	case 'foo' in
		FOO) echo 'OK' ;;
		*) echo 'NG' ;;
	esac

	[[ 'foo' == FOO ]] && echo 'OK' || echo 'NG'

	local -r parameter='foo bar'
	echo "${parameter/FOO/baz}"

}

# 
shopt -u nocasematch
f

shopt -s nocasematch
f
```

```text:実行結果
NG
NG
foo bar
OK
OK
baz bar
```

ちなみに、パラメータ展開のパターン削除および大文字小文字変換は影響を受けないため注意:

- パラメータ展開
    - パターン削除
        - `${parameter#pattern}`
        - `${parameter##pattern}`
        - `${parameter%pattern}`
        - `${parameter%%pattern}`
    - 大文字小文字変換
        - `${parameter^pattern}`
        - `${parameter^^pattern}`
        - `${parameter,pattern}`
        - `${parameter,,pattern}`

パターン削除で `nocasematch` を使用したい場合、パターン置換や正規表現等に書き換えます。

※シェルオプション `nocasematch` はパターンマッチングだけでなく正規表現にも影響します。

参考「[[シェル] パターン置換 "${parameter/pattern}" を用いたパターン削除 - Qiita](https://qiita.com/kerupani129/items/10d0741748ae5d5756d4)」

### 3.1.2. パターン置換の `&`

```bash
# 
function f() {

	local -r parameter='foo bar'
	echo "${parameter/foo/&}"
	echo "${parameter/foo/\&}"
	echo "${parameter/foo/'&'}"

}

#  
shopt -s patsub_replacement
f

shopt -u patsub_replacement
f
```

```text:実行結果
foo bar
& bar
& bar
& bar
& bar
& bar
```

# 4. 互換モード

シェル変数 `BASH_COMPAT` で互換モードのバージョンを指定可能です。

※互換モードのシェルオプションは廃止予定です。

|バージョン|シェルオプション|
|-|-|
|5.1|(なし)|
|4.2|`compat42`|

パターンマッチングに影響する互換性レベル:

- バージョン 5.1
    - コマンド置換 `$(command)` は解析時に拡張パターンマッチングが有効の扱いにする
- バージョン 4.2
    - 二重引用符で囲まれたパターン置換 `${parameter/pattern/string}` における置換文字列 `string` は引用符の削除を行わない

参考「[[Bash] 互換モード - Qiita](https://qiita.com/kerupani129/items/04b03a2a964f96727437)」
参考「[6.12 Shell Compatibility Mode - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Shell-Compatibility-Mode)」

## 4.1. コマンド置換 `$(command)` における拡張パターンマッチング

違い:

- バージョン 5.2 以降
    - 拡張パターンマッチングが無効のときにコマンド置換 `$(command)` で拡張パターンを用いると、未実行であっても解析時にエラーが発生する
- バージョン 5.1 以前
    - コマンド置換 `$(command)` は解析時に拡張パターンマッチングが有効の扱いにする
    - 拡張パターンマッチングが無効のときにコマンド置換 `$(command)` で拡張パターンを用いても、それが実行されるまでに拡張パターンマッチングが有効化されればエラーは発生しない

※コマンド置換 `` `command` `` には影響しません。
※パラメータ展開の拡張パターンには影響しません。

```bash
# 
unset BASH_COMPAT

shopt -s extglob
function f() {

	echo "$(echo /usr/@(lib))"

	echo "$(case 'foo' in @(foo)) echo 'OK' ;; *) echo 'NG' ;; esac)"

}

shopt -s extglob
f

# 
BASH_COMPAT=5.1

shopt -u extglob
function g() {

	echo "$(echo /usr/@(lib))"

	echo "$(case 'foo' in @(foo)) echo 'OK' ;; *) echo 'NG' ;; esac)"

}

shopt -s extglob
g
```

```text:実行結果
/usr/lib
OK
/usr/lib
OK
```

## 4.2. 二重引用符で囲まれたパターン置換 `${parameter/pattern/string}` における置換文字列 `string`

違い:

- バージョン 4.3 以降
    - 引用符の削除を行う
- バージョン 4.2 以前
    - 引用符の削除を行わない

```bash
# 
function f() {

	local -r parameter='foo bar'
	echo "${parameter/foo/'baz'}"

}

#  
unset BASH_COMPAT
f

BASH_COMPAT=4.2
f
```

```bash
# 
function f() {

	local -r parameter='foo bar'
	echo "${parameter/foo/'baz'}"

}

#  
shopt -u compat42
f

shopt -s compat42
f
```

```text:実行結果
baz bar
'baz' bar
```

二重引用符内だけでなく、ヒアドキュメント内も影響します。

```bash
# 
function f() {

	local -r parameter='foo bar'
	cat <<-EOF
		${parameter/foo/'baz'}
	EOF

}

#  
unset BASH_COMPAT
f

BASH_COMPAT=4.2
f
```

```bash
# 
function f() {

	local -r parameter='foo bar'
	cat <<-EOF
		${parameter/foo/'baz'}
	EOF

}

#  
shopt -u compat42
f

shopt -s compat42
f
```

```text:実行結果
baz bar
'baz' bar
```