[Bash] 互換モード

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

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

|バージョン|シェルオプション|
|-|-|
|5.1|(なし)|
|5.0|(なし)|
|4.4|`compat44`|
|4.3|`compat43`|
|4.2|`compat42`|
|4.1|`compat41`|
|4.0|`compat40`|
|3.2|`compat32`|
|3.1|`compat31`|

参考「[6.12 Shell Compatibility Mode - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Shell-Compatibility-Mode)」

# まとめ

互換性レベル:

- バージョン 5.1
    - `unset 'array[@]'` を `unset array` と同じ意味にする
    - 算術式内の展開を複数回行う
        - 算術コマンド `(( expression ))` 
        - 算術 `for` 文 `for (( expr1 ; expr2 ; expr3 ))`
        - 条件コマンド `[[ expression ]]` の算術演算子の引数
        - パラメータ展開の部分列 `${parameter:offset}` および `${parameter:offset:length}`
        - 算術式展開 `$(( expression ))`
        - インデックス配列の添字
    - 条件コマンド `[[ expression ]]` の条件式 `-v array[subscript]` のインデックス配列および連想配列 `array` の添字 `subscript` の展開を複数回行う
        - ※ Bash Reference Manual に書かれていないが、**COMPAT** ファイルに書かれている
    - 条件式 `-v 'assoc_array[@]'` を `-v 'indexed_array[@]'` と同じ意味にする
    - パラメータ展開のデフォルト値代入 `${parameter=value}` および `${parameter:=value}` で代入時に変換前の値を返す
    - コマンド置換 `$(command)` は解析時に拡張パターンマッチングが有効の扱いにする
- バージョン 5.0
    - シェル変数 `RANDOM` の乱数の生成方法を変更する
    - コマンドハッシュテーブルが空の場合、`hash -l` の出力を引数なしの `hash` の出力と同じにする
        - ※ **POSIX モードが無効の場合**
        - ※ `hash -l` の出力形式に関する POSIX モードの影響は (おそらく) 文書化されていないため注意
    - ヒアドキュメントおよびヒアストリングで一時ファイルの使用を強制する
        - ※ Bash Reference Manual に書かれていないが、**COMPAT** ファイルに書かれている
- バージョン 4.4
    - シェル変数 `BASH_ARGV` および `BASH_ARGC` を初期化するタイミングを変更する
    - ループ内のサブシェルにおける `break` または `continue` コマンドはサブシェルを終了する
    - 関数内の `export` および `readonly` コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合にシェル環境に影響する
        - **※ POSIX モードが無効の場合**
- バージョン 4.3
    - 配列の複合代入が引用符で囲まれている場合に警告を表示しない
        - `declare name[subscript]='(value1 value2 ... )'`
    - パラメータ展開における一部の "bad substitution" エラーは致命的でないエラーとみなす
        - **※ POSIX モードが有効の場合**
    - 関数内の `break` および `continue` コマンドは、関数呼び出しがあるループに影響する
- バージョン 4.2
    - 二重引用符で囲まれたパターン置換 `${parameter/pattern/string}` における置換文字列 `string` は引用符の削除を行わない
    - 二重引用符で囲まれたパラメータ展開 `${...}` 内の文字列において、一重引用符で囲まれた範囲を異なる文脈で展開する
        - **※ POSIX モードが有効の場合**
- バージョン 4.1
    - オプションが与えられている `time` はディスクコマンドでなく予約語とみなす
        - **※ POSIX モードが有効の場合**
    - 二重引用符で囲まれたパラメータ展開 `${...}` 内の文字列において、解析時に一重引用符の対応の確認を行う
        - **※ POSIX モードが有効の場合**
- バージョン 4.0
    - 条件コマンド `[[ expression ]]` の `<` および `>` 演算子はロケールを考慮せず ASCII 順序で文字列比較する
- バージョン 3.2
    - 対話モードでサブシェルでないコマンドリスト内のコマンドをシグナル `SIGINT` で終了したとき、コマンドリスト全体の実行を中断しない
        - `a; b; c` 等
- バージョン 3.1
    - 条件コマンド `[[ expression ]]` の `=~` 演算子の右辺はクォートされても効果なしにする

参考「[6.12 Shell Compatibility Mode - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Shell-Compatibility-Mode)」
参考「[COMPAT - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/COMPAT)」

# バージョン 5.1

## 1. `unset 'array[@]'`

違い:

- バージョン 5.2 以降
    - `unset 'assoc_array[@]'` は連想配列 `assoc_array` の要素 `assoc_array[@]` を `unset` する
        - `unset assoc_array` しない
    - `unset 'indexed_array[@]'` はインデックス配列 `indexed_array` の要素を全て `unset` する
        - `unset indexed_array` しない
        - `indexed_array=()` と同じ意味
- バージョン 5.1 以前
    - `unset 'array[@]'` は `unset array` と同じ意味

※コマンドの引数に添字をともなう配列変数を指定する場合、パス名展開されないよう注意する必要があります。

### 1.1. 連想配列

```bash
# 
function f() {

	local -A assoc_array=([@]=foo [x]=bar)

	unset 'assoc_array[@]'
	declare -p assoc_array

}

BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f

# 
function g() {

	local -A assoc_array=([@]=foo [x]=bar)

	unset assoc_array
	declare -p assoc_array

}

g
```

```text:実行結果
declare -A assoc_array=([x]="bar" )
declare -- assoc_array
declare -- assoc_array
```

### 1.2. インデックス配列

```bash
# 
function f() {

	local -a indexed_array=(foo bar baz)

	unset 'indexed_array[@]'
	declare -p indexed_array

}

BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f

# 
function g() {

	local -a indexed_array=(foo bar baz)

	indexed_array=()
	declare -p indexed_array

	unset indexed_array
	declare -p indexed_array

}

g
```

```text:実行結果
declare -a indexed_array=()
declare -- indexed_array
declare -a indexed_array=()
declare -- indexed_array
```

## 2. 算術式内の展開

違い:

- バージョン 5.2 以降
    - 算術式内の展開は 1 回のみ行う
- バージョン 5.1 以前
    - 算術式内の展開は複数回行う

連想配列の添字はシェルオプション `assoc_expand_once` の影響を受ける場合があります。

シェルオプション `assoc_expand_once` については別記事にしました。

参考「[[Bash] シェルオプション assoc_expand_once - Qiita](https://qiita.com/kerupani129/items/90a136c2c101e33b25ad)」

※本記事ではパラメータ展開の例を記載しますが、コマンド置換等の他の展開も同様の扱いになります。
※本記事では連想配列の添字の例を記載しますが、連想配列の添字以外の展開も同様の扱いになります。

### 2.1. 算術コマンド `(( expression ))`

```bash
# 
function f() {

	local -rAi assoc_array=([x]=1 ['$x']=0)
	local -r x='x'

	local -r subscript='$x'
	(( assoc_array[$subscript] )) && echo 'OK' || echo 'NG'

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
NG
OK
```

### 2.2. 算術 `for` 文 `for (( expr1 ; expr2 ; expr3 ))`

```bash
# 
function f() {

	local -rAi assoc_array=([x]=23 ['$x']=42)
	local -r x='x'

	local -r subscript='$x'
	local -i i
	for (( i = assoc_array[$subscript]; i < 1 + assoc_array[$subscript]; i++ )); do
		echo "$i"
	done
	unset i

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
42
23
```

### 2.3. 条件コマンド `[[ expression ]]` の算術演算子の引数

|条件式の算術演算子|算術式の演算子|意味|
|-|-|-|
|`-eq`|`==`|等値|
|`-ne`|`!=`|非等値|
|`-lt`|`<`|小なり|
|`-le`|`<=`|小なりイコール|
|`-gt`|`>`|大なり|
|`-ge`|`>=`|大なりイコール|

```bash
# 
function f() {

	local -rAi assoc_array=([x]=1 ['$x']=0)
	local -r x='x'

	local -r subscript='$x'
	[[ assoc_array[$subscript] -ne 0 ]] && echo 'OK' || echo 'NG'

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
NG
OK
```

### 2.4. パラメータ展開の部分列 `${parameter:offset}` および `${parameter:offset:length}`

```bash
# 
function f() {

	local -r parameter=foo

	local -rAi assoc_array=([x]=0 ['$x']=1)
	local -r x='x'

	local -r subscript='$x'
	echo "${parameter:assoc_array[$subscript]}"

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f

# 
function g() {

	local -r parameter=foo

	local -rAi assoc_array=([x]=3 ['$x']=2)
	local -r x='x'

	local -r subscript='$x'
	echo "${parameter:0:assoc_array[$subscript]}"

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
g
BASH_COMPAT=5.1
g
```

```text:実行結果
oo
foo
fo
foo
```

### 2.5. 算術式展開 `$(( expression ))`

```bash
# 
function f() {

	local -rAi assoc_array=([x]=23 ['$x']=42)
	local -r x='x'

	local -r subscript='$x'
	echo "$(( assoc_array[$subscript] ))"

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
42
23
```

### 2.6. インデックス配列の添字

```bash
# 
function f() {

	local -a indexed_array=(foo bar)

	local -rAi assoc_array=([x]=0 ['$x']=1)
	local -r x='x'

	local -r subscript='$x'
	indexed_array[assoc_array[$subscript]]=baz

	echo "${indexed_array[@]}"

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f

# 
function g() {

	local -a indexed_array=(foo bar)

	local -rAi assoc_array=([x]=0 ['$x']=1)
	local -r x='x'

	local -r subscript='$x'
	echo "${indexed_array[assoc_array[$subscript]]}"

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
g
BASH_COMPAT=5.1
g
```

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

## 3. 条件コマンド `[[ expression ]]` の条件式 `-v array[subscript]` の `subscript` の展開

違い:

- バージョン 5.2 以降
    - 配列の添字の展開は 1 回のみ行う
- バージョン 5.1 以前
    - 配列の添字の展開は複数回行う

連想配列の添字はシェルオプション `assoc_expand_once` の影響を受ける場合があります。

シェルオプション `assoc_expand_once` については別記事にしました。

参考「[[Bash] シェルオプション assoc_expand_once - Qiita](https://qiita.com/kerupani129/items/90a136c2c101e33b25ad)」

※本記事ではパラメータ展開の例を記載しますが、コマンド置換等の他の展開も同様の扱いになります。
※条件コマンド `[[ expression ]]` ではパス名展開は行われません。

### 3.1. インデックス配列

```bash
# 
function f() {

	local -ra indexed_array=(foo)
	local -r x='0'

	local -r subscript='$x'
	[[ -v indexed_array[$subscript] ]] && echo 'OK' || echo 'NG'

}

BASH_COMPAT=5.2
(f 2> /dev/null) || echo 'Error'
BASH_COMPAT=5.1
(f 2> /dev/null) || echo 'Error'
```

```text:実行結果
Error
OK
```

### 3.2. 連想配列

```bash
# 
function f() {

	local -rA assoc_array=([x]=foo)
	local -r x='x'

	local -r subscript='$x'
	[[ -v assoc_array[$subscript] ]] && echo 'OK' || echo 'NG'

}

shopt -u assoc_expand_once
BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
NG
OK
```

## 4. 条件式 `-v 'assoc_array[@]'`

違い:

- バージョン 5.2 以降
    - 条件式 `-v 'assoc_array[@]'` は連想配列の要素 `assoc_array[@]` に値が割り当てられているかを表す
- バージョン 5.1 以前
    - 条件式 `-v 'assoc_array[@]'` は `-v 'indexed_array[@]'` と同じ意味

※条件式 `-v array` は `array` が連想配列かインデックス配列かに関わらず条件式 `-v 'array[0]'` と同じ意味になります。
※コマンドの引数に添字をともなう配列変数を指定する場合、パス名展開されないよう注意する必要があります。
※条件コマンド `[[ expression ]]` ではパス名展開は行われません。

```bash
# 
function f() {

	local -rA assoc_array=([x]=foo)

	test -v 'assoc_array[@]' && echo 'OK' || echo 'NG'
	[ -v 'assoc_array[@]' ] && echo 'OK' || echo 'NG'
	[[ -v assoc_array[@] ]] && echo 'OK' || echo 'NG'

}

BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f

# 
function g() {

	local -ra indexed_array=(foo)

	test -v 'indexed_array[@]' && echo 'OK' || echo 'NG'
	[ -v 'indexed_array[@]' ] && echo 'OK' || echo 'NG'
	[[ -v indexed_array[@] ]] && echo 'OK' || echo 'NG'

}

g
```

```text:実行結果
NG
NG
NG
OK
OK
OK
OK
OK
OK
```

ちなみにバージョン 5.2 以降では、連想配列が要素を持つかどうかを条件式 `-v 'assoc_array[@]'` で確認出来ないため、パラメータ展開 `${#assoc_array[@]}` を用いて要素数で確認します。

```bash
# 
function f() {

	local -rA assoc_array=([x]=foo)

	echo "${#assoc_array[@]}"
	test "${#assoc_array[@]}" -gt 0 && echo 'OK' || echo 'NG'
	[ "${#assoc_array[@]}" -gt 0 ] && echo 'OK' || echo 'NG'
	[[ ${#assoc_array[@]} -gt 0 ]] && echo 'OK' || echo 'NG'

}

f
```

```text:実行結果
1
OK
OK
OK
```

## 5. パラメータ展開のデフォルト値代入 `${parameter=value}` および `${parameter:=value}`

違い:

- バージョン 5.2 以降
    - デフォルト値代入時に**変換後**の値を返す
    - **変換後**の値が代入される
- バージョン 5.1 以前
    - デフォルト値代入時に**変換前**の値を返す
    - **変換後**の値が代入される

大文字属性または小文字属性が設定された変数に対して変数代入を用いて代入する場合、大文字小文字が変換されます。

```bash
# 
function f() {

	local -l x='FOO'
	echo "$x"

	local -u y='bar'
	echo "$y"

}

f
```

```text:実行結果
foo
BAR
```

```bash
# 
function f() {

	local -l x
	echo "${x=FOO}"
	echo "$x"

	local -u y
	echo "${y=bar}"
	echo "$y"

}

BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
foo
foo
BAR
BAR
FOO
foo
bar
BAR
```

```bash
# 
function f() {

	local -l x=''
	echo "${x:=FOO}"
	echo "$x"

	local -u y=''
	echo "${y:=bar}"
	echo "$y"

}

BASH_COMPAT=5.2
f
BASH_COMPAT=5.1
f
```

```text:実行結果
foo
foo
BAR
BAR
FOO
foo
bar
BAR
```

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

違い:

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

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

```bash
# 
BASH_COMPAT=5.2

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
```

# バージョン 5.0

## 1. シェル変数 `RANDOM`

バージョン 5.1 以降、よりランダム性の高い生成方法に変更されました。

```bash
# 
function f() {

	RANDOM=0

	local -i i
	for (( i = 0 ; i < 3 ; i++ )); do
		echo "$RANDOM"
	done
	unset i

}

BASH_COMPAT=5.1
f
BASH_COMPAT=5.0
f
```

```text:実行結果
20814
24386
149
20034
24315
12703
```

## 2. コマンドハッシュテーブルが空の場合の `hash -l`

違い:

- バージョン 5.1 以降
    - 何も表示しない
- バージョン 5.0 以前
    - 引数なしの `hash` と同じ内容を表示する

※ POSIX モードが有効の場合、バージョンに関わらず `hash -l` も引数なしの `hash` も何も表示しません。
※ `hash -l` の出力形式に関する POSIX モードの影響は (おそらく) 文書化されていないため注意が必要です。

```bash
set +o posix

# 
function f() {

	hash -r

	echo "$(hash -l)"

}

BASH_COMPAT=5.1
f
BASH_COMPAT=5.0
f

# 
function g() {

	hash -r

	echo "$(hash)"

}

g
```

```text:実行結果

hash: hash table empty
hash: hash table empty
```

参考「[hash.def\builtins - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/hash.def?h=bash-5.1)」(バージョン 5.1)

## 3. ヒアドキュメントおよびヒアストリング

違い:

- バージョン 5.1 以降
    - パイプバッファのサイズ以下ならパイプを使用する
    - パイプバッファのサイズ超なら一時ファイルを使用する
- バージョン 5.0 以前
    - 一時ファイルを使用する

```bash
# 
function f() {

	echo "BASH_COMPAT=$BASH_COMPAT"

	strace -f bash -c $'cat <<EOF\nfoo\nEOF\ncat <<< bar' |& grep '/tmp/' || :

}

export BASH_COMPAT=5.1
f
export BASH_COMPAT=5.0
f
```

```text:実行結果の例
BASH_COMPAT=5.1
BASH_COMPAT=5.0
[pid  7208] openat(AT_FDCWD, "/tmp/sh-thd.MD3H1J", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid  7208] openat(AT_FDCWD, "/tmp/sh-thd.MD3H1J", O_RDONLY) = 4
[pid  7208] unlink("/tmp/sh-thd.MD3H1J") = 0
[pid  7209] openat(AT_FDCWD, "/tmp/sh-thd.7O7HmK", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid  7209] openat(AT_FDCWD, "/tmp/sh-thd.7O7HmK", O_RDONLY) = 4
[pid  7209] unlink("/tmp/sh-thd.7O7HmK") = 0
```

パイプバッファのサイズは `ulimit` コマンドで確認出来ます。

```bash
echo "$(( $(ulimit -p) * 512 )) bytes"
```

```text:実行結果の例
4096 bytes
```

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

# バージョン 4.4

## 1. シェル変数 `BASH_ARGV` および `BASH_ARGC`

サブルーチンは以下のものがあります:

- シェル関数
- `.` コマンドまたは `source` コマンドによって実行されるシェルスクリプト

**サブルーチン**における挙動が変化します:

- バージョン 5.0 以降
    - 拡張デバッグモードを有効化時に初期化される: 位置パラメータ
- バージョン 4.4 以前
    - 実行開始時に初期化される
        - 拡張デバッグモードが無効の場合: 空
        - 拡張デバッグモードが有効の場合: 位置パラメータ

※初めから拡張デバッグモードを有効にする場合、内部的な処理方法は変わりますが見かけの挙動は変化しません。

```bash
# 
function f() {
	shopt -s extdebug
	echo "${BASH_ARGV[@]}"
	echo "${BASH_ARGC[@]}"
}

BASH_COMPAT=5.0
(shopt -u extdebug; f foo)
BASH_COMPAT=4.4
(shopt -u extdebug; f foo)

BASH_COMPAT=5.0
(shopt -s extdebug; f foo)
BASH_COMPAT=4.4
(shopt -s extdebug; f foo)
```

```text:実行結果
foo
1

0
foo
1 0
foo
1 0
```

バージョン 5.0 以降、**メインルーチン**における内部的な処理方法が変更されましたが、互換モードを使用しても内部的な処理方法は**変化しません**:

- バージョン 5.0 以降
    - 拡張デバッグモードを有効化時に初期化される: 位置パラメータ
    - 下位互換のため、拡張デバッグモードが無効の場合、参照時に動的に初期化される: 位置パラメータ
- バージョン 4.4 以前
    - 実行開始時に初期化される: 位置パラメータ

```bash
bash -c 'shopt -u extdebug; echo "${BASH_ARGV[@]}"; echo "${BASH_ARGC[@]}"' bash foo
bash -c 'shopt -s extdebug; echo "${BASH_ARGV[@]}"; echo "${BASH_ARGC[@]}"' bash foo
```

```text:実行結果
foo
1
foo
1
```

参考「[variables.c - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/variables.c?h=bash-5.0)」(バージョン 5.0)
参考「[NEWS - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/NEWS?h=bash-5.0)」(バージョン 5.0)
参考「[changelog\CWRU - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/CWRU/changelog?h=bash-5.0)」(バージョン 5.0)

以下の場合に取得した値が正しくないことがあります:

- 途中で拡張デバッグモードを有効化する場合
- 拡張デバッグモードが無効のままの場合

```bash
# 
function f() {
	shopt -s extdebug
	echo "${BASH_ARGV[@]}"
	echo "${BASH_ARGC[@]}"
}

BASH_COMPAT=5.0
(shopt -u extdebug; f foo)
BASH_COMPAT=4.4
(shopt -u extdebug; f foo)
```

```text:実行結果
foo
1

0
```

```bash
export BASH_COMPAT=5.0
bash -c 'shopt -u extdebug; f() { :; }; f; echo "${BASH_ARGV[@]}"; echo "${BASH_ARGC[@]}"' bash foo
export BASH_COMPAT=4.4
bash -c 'shopt -u extdebug; f() { :; }; f; echo "${BASH_ARGV[@]}"; echo "${BASH_ARGC[@]}"' bash foo
```

```text:実行結果
foo
1

0
```

参考「[BASH_ARGC - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#index-BASH_005fARGC)」
参考「[BASH_ARGV - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#index-BASH_005fARGV)」

## 2. ループ内のサブシェルにおける `break` または `continue` コマンド

ループは以下のものがあります:

- `for` コマンド
- `while` コマンド
- `until` コマンド
- `select` コマンド

違い:

- バージョン 5.0 以降
    - 何もしない
    - ループ外でコマンドを実行するのと同じ扱い
- バージョン 4.4 以前
    - サブシェルを終了する
    - `break` コマンドはループを終了しない
    - `continue` コマンドは次のループを開始しない

※バージョン 4.4 以前でも本来の正しいコマンドの挙動にならないため、そもそも**サブシェル内で `break` または `continue` コマンドを使用しないべき**です。

```bash
# 
function f() {

	for x in foo bar baz; do
		(break; echo -n "$x ")
		echo -n '_ '
	done
	echo

}

BASH_COMPAT=5.0
f
BASH_COMPAT=4.4
f

# 
function g() {

	for x in foo bar baz; do
		(continue; echo -n "$x ")
		echo -n '_ '
	done
	echo

}

BASH_COMPAT=5.0
g
BASH_COMPAT=4.4
g

# 
function h() {

	for x in foo bar baz; do
		(exit; echo -n "$x ")
		echo -n '_ '
	done
	echo

}

h
```

```text:実行結果
foo _ bar _ baz _
_ _ _
foo _ bar _ baz _
_ _ _
_ _ _
```

## 3. 関数内の `export` および `readonly` コマンドの前にある変数代入

### 3.1. 概要

関数呼び出しの前に変数代入がある場合の違い:

- バージョン 5.0 以降
    - POSIX モードが無効の場合、シェル環境に影響しない
    - POSIX モードが有効の場合、シェル環境に影響する
- バージョン 4.4 以前
    - シェル環境に影響する

※関数呼び出しの前に変数代入がない場合、(POSIX モードかどうかやバージョンに関わらず) シェル環境に影響します。

```bash
set +o posix

# 
function f() {

	x=baz export x

}

BASH_COMPAT=5.0
(x=foo; x=bar f; echo "$x")
BASH_COMPAT=4.4
(x=foo; x=bar f; echo "$x")

# 
function g() {

	x=baz readonly x

}

BASH_COMPAT=5.0
(x=foo; x=bar g; echo "$x")
BASH_COMPAT=4.4
(x=foo; x=bar g; echo "$x")
```

```text:実行結果
foo
baz
foo
baz
```

### 3.2. 詳細

コマンドの前にある変数代入はコマンドが参照する環境に影響し、基本的にシェル環境に影響しません。

```bash
(x=foo; x=bar bash -c 'echo "$x"'; echo "$x")
```

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

参考「[3.7.1 Simple Command Expansion - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#index-command-expansion)」
参考「[3.7.4 Environment - Bash Reference Manual](https://www.gnu.org/software/bash/manual/bash.html#Environment)」

POSIX モードが無効の場合、`export` および `readonly` コマンドの前にある変数代入も基本的にシェル環境に影響しません。

ただし、それらのコマンドの引数で変数を指定する場合、コマンド自体がシェル環境に影響するためコマンドの前にある変数代入はシェル環境に影響します。

```bash
set +o posix

(x=foo; x=bar export > /dev/null; echo "$x")
(x=foo; x=bar export x; echo "$x")

(x=foo; x=bar readonly > /dev/null; echo "$x")
(x=foo; x=bar readonly x; echo "$x")
```

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

シェル関数の環境は以下のような挙動をします:

- 基本的に呼び出し元の環境と同じ
- 基本的に呼び出し元と変数およびその値が共有される

```bash
function f() {

	x=bar

}

(x=foo; f; echo "$x")
```

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

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

関数呼び出しの前に変数代入がある場合は基本的にシェル環境に影響しません。

```bash
function f() {

	x=bar

}

(x=foo; x=bar f; echo "$x")
```

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

参考「[changelog\CWRU - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/CWRU/changelog?h=bash-5.0)」(バージョン 5.0)
参考「[changelog\CWRU - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/CWRU/changelog?h=bash-5.1)」(バージョン 5.1)
参考「[variables.c - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/variables.c?h=bash-5.1)」(バージョン 5.1)
参考「[0000654: unclear behavior of in-line variable assignments preceding functions, special built-ins - Austin Group Defect Tracker](https://austingroupbugs.net/view.php?id=654)」(POSIX 解釈 654)

関数内で `export` および `readonly` コマンドを実行すると、関数呼び出しの前に変数代入がある場合でも関数呼び出しのシェル環境に影響します。

```bash
# 
function f() {

	export x

}

(x=foo; x=bar f; echo "$x")

# 
function g() {

	readonly x

}

(x=foo; x=bar g; echo "$x")
```

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

POSIX モードが無効のとき、バージョン 5.0 以降、関数内の `export` および `readonly` コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合、一時環境に影響しますがシェル環境に影響しません。

POSIX モードが無効のとき、バージョン 4.4 以前、関数内の `export` および `readonly` コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合、シェル環境に影響します。

```bash
set +o posix

# 
function f() {

	x=baz export x

}

BASH_COMPAT=5.0
(x=foo; x=bar f; echo "$x")
BASH_COMPAT=4.4
(x=foo; x=bar f; echo "$x")

# 
function g() {

	x=baz readonly x

}

BASH_COMPAT=5.0
(x=foo; x=bar g; echo "$x")
BASH_COMPAT=4.4
(x=foo; x=bar g; echo "$x")
```

```text:実行結果
foo
baz
foo
baz
```

### 3.3. おまけ: POSIX モードが有効の場合

POSIX モードが有効の場合、特殊組み込みコマンドの前にある変数代入はシェル環境に影響します。

特殊組み込みコマンド:

- `:`
- `.`
- `break`
- `continue`
- `eval`
- `exec`
- `exit`
- `export`
- `readonly`
- `return`
- `set `
- `shift`
- `trap`
- `unset`

```bash
set -o posix
(x=foo; x=bar :; echo "$x")
set +o posix
(x=foo; x=bar :; echo "$x")
```

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

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

POSIX モードが有効の場合、`export` および `readonly` コマンドも特殊組み込みコマンドのため、それらのコマンドの前にある変数代入はシェル環境に影響します。

```bash
set -o posix

(x=foo; x=bar export > /dev/null; echo "$x")
(x=foo; x=bar export x; echo "$x")

(x=foo; x=bar readonly > /dev/null; echo "$x")
(x=foo; x=bar readonly x; echo "$x")
```

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

POSIX モードが有効のとき、関数内の `export` および `readonly` コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合、シェル環境に影響します。

```bash
set -o posix

# 
function f() {

	x=baz export x

}

(x=foo; x=bar f; echo "$x")

# 
function g() {

	x=baz readonly x

}

(x=foo; x=bar g; echo "$x")
```

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

# バージョン 4.3

## 1. 引用符で囲まれている配列の複合代入

違い:

- バージョン 4.4 以降
    - 警告を表示する
        - `declare name[subscript]='(value1 value2 ... )'`
- バージョン 4.3 以前
    - 警告を表示しない

※本記事ではインデックス配列の例を記載しますが、連想配列も同様の扱いになります。

```bash
# 
function f() {

	echo "BASH_COMPAT=$BASH_COMPAT"

	bash -c $'declare array[0]=\'(foo bar baz)\''
	bash -c $'declare array=\'\'; declare array[0]=\'(foo bar baz)\''

}

export BASH_COMPAT=4.4
f
export BASH_COMPAT=4.3
f
```

```text:実行環境
BASH_COMPAT=4.4
bash: line 1: warning: array[1]=(foo bar baz): quoted compound array assignment deprecated
bash: line 1: warning: array[1]=(foo bar baz): quoted compound array assignment deprecated
BASH_COMPAT=4.3
```

配列の複合代入自体の挙動も変化するため注意が必要です。

```bash
BASH_COMPAT=4.4
(declare array[0]='(foo bar baz)' 2> /dev/null; declare -p array)
(declare array=''; declare array[0]='(foo bar baz)' 2> /dev/null; declare -p array)

BASH_COMPAT=4.3
(declare array[0]='(foo bar baz)' 2> /dev/null; declare -p array)
(declare array=''; declare array[0]='(foo bar baz)' 2> /dev/null; declare -p array)
```

```text:実行結果
declare -a array=([0]="(foo bar baz)")
declare -a array=([0]="(foo bar baz)")
declare -a array=([0]="foo" [1]="bar" [2]="baz")
declare -a array=([0]="foo" [1]="bar" [2]="baz")
```

バージョン 4.4 以降でも以下の場合には警告は表示されません:

- 既に配列変数が存在する
- 複合代入時に配列属性 `-a` または `-A` を設定する
- 関数内でローカル変数を作成する

```bash
# 
function f() {

	echo "BASH_COMPAT=$BASH_COMPAT"

	bash -c $'declare -a array=(); declare array[0]=\'(foo bar baz)\''
	bash -c $'declare -a array[0]=\'(foo bar baz)\''
	bash -c $'f() { declare -a array[0]=\'(foo bar baz)\'; }; f'

}

export BASH_COMPAT=4.4
f
export BASH_COMPAT=4.3
f
```

```text:実行環境
BASH_COMPAT=4.4
BASH_COMPAT=4.3
```

参考「[changelog\CWRU - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/CWRU/changelog?h=bash-4.4)」(バージョン 4.4)

バージョン 4.4 以降、関数内でローカル変数を作成する場合に警告が表示されない理由は以下の通りです:

- `declare name[subscript]` とすると (配列属性 `-a` または `-A` を設定しなくても) 配列変数が作成される
- `declare` コマンドでは以下の順で処理する
    1. ローカル配列変数を作成する
    1. 配列変数の存在有無を確認する
    1. グローバル配列変数を作成する
    1. 配列変数が存在しない等の条件を満たす場合に警告を表示する

参考「[declare.def\builtins - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/declare.def?h=bash-4.4)」(バージョン 4.4)

## 2. パラメータ展開における一部の "bad substitution" エラー

違い:

- バージョン 4.4 以降
    - POSIX モードが無効の場合、致命的でないエラー
    - POSIX モードが有効の場合、致命的なエラー
- バージョン 4.3 以前
    - 致命的でないエラー

参考「[changelog\CWRU - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/CWRU/changelog?h=bash-4.4)」(バージョン 4.4)
参考「[Bash does not exit on non-interactive "Bad substitution" errors](https://lists.gnu.org/archive/html/bug-bash/2015-08/msg00013.html)」

```bash
set +e -o posix
export SHELLOPTS

export BASH_COMPAT=4.4
bash -c $': "${}"\necho foo' || :
export BASH_COMPAT=4.3
bash -c $': "${}"\necho foo' || :
```

```text:実行結果
bash: line 1: ${}: bad substitution
bash: line 1: ${}: bad substitution
foo
```

全ての "bad substitution" エラーには影響しません。

参考「[subst.c - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/subst.c?h=bash-4.4)」(バージョン 4.4)

## 3. 関数内の `break` および `continue` コマンド

ループは以下のものがあります:

- `for` コマンド
- `while` コマンド
- `until` コマンド
- `select` コマンド

ループ内に関数呼び出しがある場合の違い:

- バージョン 4.4 以降
    - ループに影響しない
- バージョン 4.3 以前
    - ループに影響する

```bash
# 
function f() {
	break
}

function g() {

	for x in foo bar baz; do
		f
		echo -n "$x "
	done
	echo

}

BASH_COMPAT=4.4
g
BASH_COMPAT=4.3
g

# 
function h() {
	continue
}

function i() {

	for x in foo bar baz; do
		h
		echo -n "$x "
	done
	echo

}

BASH_COMPAT=4.4
i
BASH_COMPAT=4.3
i
```

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

foo bar baz

```

# バージョン 4.2

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

違い:

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

```bash
# 
function f() {

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

}

BASH_COMPAT=4.3
f
BASH_COMPAT=4.2
f
```

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

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

```bash
# 
function f() {

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

}

BASH_COMPAT=4.3
f
BASH_COMPAT=4.2
f
```

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

参考「[subst.c - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/subst.c?h=bash-4.3)」(バージョン 4.3)

## 2. 二重引用符で囲まれたパラメータ展開 `${...}` 内の文字列

影響する場所は以下の表の通りです。

|説明|パラメータ展開|文字列|
|-|-|-|
|デフォルト値|`${parameter:-word}`|`word`|
|デフォルト値代入|`${parameter:=word}`|`word`|
|エラー表示|`${parameter:?word}`|`word`|
|代替値|`${parameter:+word}`|`word`|

違い:

- バージョン 4.3 以降
    - POSIX モードが無効の場合
        - 一重引用符で囲まれた範囲を異なる文脈で展開する
        - 解析時に一重引用符の対応の確認を行わない
    - POSIX モードが有効の場合、一重引用符は特別でない
- バージョン 4.2
    - 一重引用符で囲まれた範囲を異なる文脈で展開する
    - 解析時に一重引用符の対応の確認を行わない
- バージョン 4.1 以前
    - 一重引用符で囲まれた範囲を異なる文脈で展開する
    - 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
    - ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない

パターン置換 `${parameter/pattern/string}` の置換文字列 `string` では、(POSIX モードかどうかやバージョンに関わらず) 以下の挙動になります:

- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
- ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない

※パターンには影響しません。

```bash
set -o posix
export SHELLOPTS

# 
function f() {

	local -xr x=''
	echo "${x:-foo'}'bar}"

	local y=''
	echo "${y:=foo'}'bar}"

	bash -c $'echo "${x:?foo\'}\'bar}"' || :

	local -r z=baz
	echo "${z:+foo'}'bar}"

}

export BASH_COMPAT=4.3
f
export BASH_COMPAT=4.2
f
```

```text:実行結果
foo''bar}
foo''bar}
bash: line 1: x: foo
foo''bar}
foo'}'bar
foo'}'bar
bash: line 1: x: foo}bar
foo'}'bar
```

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

```bash
set -o posix
export SHELLOPTS

# 
function f() {

	local -xr x=''
	cat <<-EOF
		${x:-foo'}'bar}
	EOF

	local y=''
	cat <<-EOF
		${y:=foo'}'bar}
	EOF

	bash -c $'cat <<EOF\n${x:?foo\'}\'bar}\nEOF' || :

	local -r z=baz
	cat <<-EOF
		${z:+foo'}'bar}
	EOF

}

export BASH_COMPAT=4.3
f
export BASH_COMPAT=4.2
f
```

```text:実行結果
foo''bar}
foo''bar}
bash: line 1: x: foo
foo''bar}
foo'}'bar
foo'}'bar
bash: line 1: x: foo}bar
foo'}'bar
```

参考「[subst.c - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/subst.c?h=bash-4.3)」(バージョン 4.3)
参考「[0000221: poor wording about even quotes in double quoted parameter expansion - Austin Group Defect Tracker](https://austingroupbugs.net/view.php?id=221)」(POSIX 解釈 221)
参考「[2.2.3 Double-Quotes - Shell Command Language](https://pubs.opengroup.org/onlinepubs/9699919799.2016edition/utilities/V3_chap02.html#tag_18_02_03)」(2016 年版)

# バージョン 4.1

## 1. オプションが与えられている `time`

違い:

- バージョン 4.2 以降
    - POSIX モードが無効の場合、予約語
    - POSIX モードが有効の場合、ディスクコマンド
- バージョン 4.1 以前
    - 予約語

※バージョン 4.2 以降、POSIX モードが有効の場合はオプション `-p` が与えられた場合でも予約語でなくディスクコマンドとみなされるため注意が必要です。

```bash
set -o posix

BASH_COMPAT=4.2
(time -V) &> /dev/null && echo 'disk command' || echo 'reserved word'
(time -p ! bash -c 'exit 1') 2> /dev/null && echo 'reserved word' || echo 'disk command'
BASH_COMPAT=4.1
(time -V) &> /dev/null && echo 'disk command' || echo 'reserved word'
(time -p ! bash -c 'exit 1') 2> /dev/null && echo 'reserved word' || echo 'disk command'
```

```text:実行結果
disk command
disk command
reserved word
reserved word
```

ディスクコマンも予約語も、オプション `-p` が与えられた場合の出力形式は同じです。

```bash
env time -p bash -c ''

set +o posix
time -p ! bash -c 'exit 1'
```

```text:実行結果の例
real 0.00
user 0.00
sys 0.00
real 0.00
user 0.00
sys 0.00
```

Bash の `time` は組み込みコマンドでなく予約語です。

```bash
type -a echo
type -a time
```

```text:実行結果の例
echo is a shell builtin
echo is /usr/bin/echo
echo is /bin/echo
time is a shell keyword
time is /usr/bin/time
time is /bin/time
```

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

## 2. 二重引用符で囲まれたパラメータ展開 `${...}` 内の文字列

影響する場所は以下の表の通りです。

|説明|パラメータ展開|文字列|
|-|-|-|
|デフォルト値|`${parameter:-word}`|`word`|
|デフォルト値代入|`${parameter:=word}`|`word`|
|エラー表示|`${parameter:?word}`|`word`|
|代替値|`${parameter:+word}`|`word`|

違い:

- バージョン 4.3 以降
    - POSIX モードが無効の場合
        - 一重引用符で囲まれた範囲を異なる文脈で展開する
        - 解析時に一重引用符の対応の確認を行わない
    - POSIX モードが有効の場合、一重引用符は特別でない
- バージョン 4.2
    - 一重引用符で囲まれた範囲を異なる文脈で展開する
    - 解析時に一重引用符の対応の確認を行わない
- バージョン 4.1 以前
    - 一重引用符で囲まれた範囲を異なる文脈で展開する
    - 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
    - ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない

パターン置換 `${parameter/pattern/string}` の置換文字列 `string` では、(POSIX モードかどうかやバージョンに関わらず) 以下の挙動になります:

- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
- ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない

※パターンには影響しません。

```bash
set -o posix
export SHELLOPTS

# 
function f() {

	local -xr x=''
	bash -c $'echo "${x:-\'foo}"' || :

	local -x y=''
	bash -c $'echo "${y:=\'foo}"' || :

	bash -c $'echo "${x:?\'foo}"' || :

	local -xr z=bar
	bash -c $'echo "${z:+\'foo}"' || :

}

export BASH_COMPAT=4.2
f
export BASH_COMPAT=4.1
f
```

```text:実行結果
bash: line 1: bad substitution: no closing `}' in "${x:-'foo}"
bash: line 1: bad substitution: no closing `}' in "${y:='foo}"
bash: line 1: bad substitution: no closing `}' in "${x:?'foo}"
bash: line 1: bad substitution: no closing `}' in "${z:+'foo}"
bash: -c: line 1: unexpected EOF while looking for matching `''
bash: -c: line 1: unexpected EOF while looking for matching `''
bash: -c: line 1: unexpected EOF while looking for matching `''
bash: -c: line 1: unexpected EOF while looking for matching `''
```

解析時の挙動変更のため、未実行であってもエラーが発生します。

```bash
set -o posix
export SHELLOPTS

# 
function f() {

	echo "BASH_COMPAT=$BASH_COMPAT"

	local -xr x=''
	bash -c $'f() { : "${x:-\'foo}"; }' || :

	local -x y=''
	bash -c $'f() { : "${y:=\'foo}"; }' || :

	bash -c $'f() { : "${x:?\'foo}"; }' || :

	local -xr z=bar
	bash -c $'f() { : "${z:+\'foo}"; }' || :

}

export BASH_COMPAT=4.2
f
export BASH_COMPAT=4.1
f
```

```text:実行結果
BASH_COMPAT=4.2
BASH_COMPAT=4.1
bash: -c: line 1: unexpected EOF while looking for matching `''
bash: -c: line 1: unexpected EOF while looking for matching `''
bash: -c: line 1: unexpected EOF while looking for matching `''
bash: -c: line 1: unexpected EOF while looking for matching `''
```

参考「[parse.y - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/parse.y?h=bash-4.3)」(バージョン 4.3)

# バージョン 4.0

## 1. 条件コマンド `[[ expression ]]` の `<` および `>` 演算子

違い:

- バージョン 4.1 以降
    - ロケールを考慮して文字列比較する
- バージョン 4.0 以前
    - ロケールを考慮せず ASCII 順序で文字列比較する

```bash
export LC_COLLATE=en_US.UTF-8
echo -e 'A\na\n' | sort | xargs
BASH_COMPAT=4.1
[[ 'a' < 'A' ]] && echo 'a A' || echo 'A a'
BASH_COMPAT=4.0
[[ 'a' < 'A' ]] && echo 'a A' || echo 'A a'

export LC_COLLATE=C
echo -e 'A\na\n' | sort | xargs
[[ 'a' < 'A' ]] && echo 'a A' || echo 'A a'
```

```text:実行結果
a A
a A
A a
A a
A a
```

# バージョン 3.2

## 1. 対話モードでサブシェルでないコマンドリスト

コマンドリスト内のコマンドをシグナル `SIGINT` で終了したときの違い:

- バージョン 4.0 以降
    - コマンドリスト全体の実行を中断する
- バージョン 3.2 以前
    - コマンドリスト全体の実行を中断しない

```bash
export BASH_COMPAT=4.0
bash -i -c 'trap - SIGINT; echo foo; sleep 5 & (sleep 1; kill -s INT $!) & fg %-; echo bar' || :
export BASH_COMPAT=3.2
bash -i -c 'trap - SIGINT; echo foo; sleep 5 & (sleep 1; kill -s INT $!) & fg %-; echo bar' || :
```

```text:実行結果の例
foo
[1] 19092
[2] 19093
sleep 5

foo
[1] 19104
[2] 19105
sleep 5

bar
```

```bash
export BASH_COMPAT=4.0
bash -i -c 'trap - SIGINT; echo foo; cat || :; echo bar' || :
export BASH_COMPAT=3.2
bash -i -c 'trap - SIGINT; echo foo; cat || :; echo bar' || :
```

```text:実行結果の例
foo
^C
foo
^C
bar
```

参考「[jobs.c - bash.git - bash](https://git.savannah.gnu.org/cgit/bash.git/tree/jobs.c?h=bash-4.0)」(バージョン 4.0)

# バージョン 3.1

## 1. 条件コマンド `[[ expression ]]` の `=~` 演算子の右辺

違い:

- バージョン 3.2 以降
    - クォートされた範囲は正規表現でなく文字列とみなす
- バージョン 3.1 以前
    - クォートされても効果なし

```bash
BASH_COMPAT=3.2
[[ 'foo' =~ '^foo$' ]] && echo 'OK' || echo 'NG'
BASH_COMPAT=3.1
[[ 'foo' =~ '^foo$' ]] && echo 'OK' || echo 'NG'

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

```text:実行結果
NG
OK
OK
```

```bash
BASH_COMPAT=3.2
[[ '^foo$' =~ '^foo$' ]] && echo 'OK' || echo 'NG'
BASH_COMPAT=3.1
[[ '^foo$' =~ '^foo$' ]] && echo 'OK' || echo 'NG'

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

```text:実行結果
OK
NG
NG
```