シェル変数 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」
まとめ
互換性レベル:
- バージョン 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」
参考「COMPAT - bash.git - bash」
バージョン 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. 連想配列
#
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
declare -A assoc_array=([x]="bar" )
declare -- assoc_array
declare -- assoc_array
1.2. インデックス配列
#
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
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」
※本記事ではパラメータ展開の例を記載しますが、コマンド置換等の他の展開も同様の扱いになります。
※本記事では連想配列の添字の例を記載しますが、連想配列の添字以外の展開も同様の扱いになります。
2.1. 算術コマンド (( expression ))
#
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
NG
OK
2.2. 算術 for
文 for (( expr1 ; expr2 ; expr3 ))
#
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
42
23
2.3. 条件コマンド [[ expression ]]
の算術演算子の引数
条件式の算術演算子 | 算術式の演算子 | 意味 |
---|---|---|
-eq |
== |
等値 |
-ne |
!= |
非等値 |
-lt |
< |
小なり |
-le |
<= |
小なりイコール |
-gt |
> |
大なり |
-ge |
>= |
大なりイコール |
#
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
NG
OK
2.4. パラメータ展開の部分列 ${parameter:offset}
および ${parameter:offset:length}
#
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
oo
foo
fo
foo
2.5. 算術式展開 $(( expression ))
#
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
42
23
2.6. インデックス配列の添字
#
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
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」
※本記事ではパラメータ展開の例を記載しますが、コマンド置換等の他の展開も同様の扱いになります。
※条件コマンド [[ expression ]]
ではパス名展開は行われません。
3.1. インデックス配列
#
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'
Error
OK
3.2. 連想配列
#
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
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 ]]
ではパス名展開は行われません。
#
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
NG
NG
NG
OK
OK
OK
OK
OK
OK
ちなみにバージョン 5.2 以降では、連想配列が要素を持つかどうかを条件式 -v 'assoc_array[@]'
で確認出来ないため、パラメータ展開 ${#assoc_array[@]}
を用いて要素数で確認します。
#
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
1
OK
OK
OK
5. パラメータ展開のデフォルト値代入 ${parameter=value}
および ${parameter:=value}
違い:
- バージョン 5.2 以降
- デフォルト値代入時に変換後の値を返す
- 変換後の値が代入される
- バージョン 5.1 以前
- デフォルト値代入時に変換前の値を返す
- 変換後の値が代入される
大文字属性または小文字属性が設定された変数に対して変数代入を用いて代入する場合、大文字小文字が変換されます。
#
function f() {
local -l x='FOO'
echo "$x"
local -u y='bar'
echo "$y"
}
f
foo
BAR
#
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
foo
foo
BAR
BAR
FOO
foo
bar
BAR
#
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
foo
foo
BAR
BAR
FOO
foo
bar
BAR
6. コマンド置換 $(command)
における拡張パターンマッチング
違い:
- バージョン 5.2 以降
- 拡張パターンマッチングが無効のときにコマンド置換
$(command)
で拡張パターンを用いると、未実行であっても解析時にエラーが発生する
- 拡張パターンマッチングが無効のときにコマンド置換
- バージョン 5.1 以前
- コマンド置換
$(command)
は解析時に拡張パターンマッチングが有効の扱いにする - 拡張パターンマッチングが無効のときにコマンド置換
$(command)
で拡張パターンを用いても、それが実行されるまでに拡張パターンマッチングが有効化されればエラーは発生しない
- コマンド置換
※コマンド置換 `command`
には影響しません。
※パラメータ展開の拡張パターンには影響しません。
#
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
/usr/lib
OK
/usr/lib
OK
バージョン 5.0
1. シェル変数 RANDOM
バージョン 5.1 以降、よりランダム性の高い生成方法に変更されました。
#
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
20814
24386
149
20034
24315
12703
2. コマンドハッシュテーブルが空の場合の hash -l
違い:
- バージョン 5.1 以降
- 何も表示しない
- バージョン 5.0 以前
- 引数なしの
hash
と同じ内容を表示する
- 引数なしの
※ POSIX モードが有効の場合、バージョンに関わらず hash -l
も引数なしの hash
も何も表示しません。
※ hash -l
の出力形式に関する POSIX モードの影響は (おそらく) 文書化されていないため注意が必要です。
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
hash: hash table empty
hash: hash table empty
参考「hash.def\builtins - bash.git - bash」(バージョン 5.1)
3. ヒアドキュメントおよびヒアストリング
違い:
- バージョン 5.1 以降
- パイプバッファのサイズ以下ならパイプを使用する
- パイプバッファのサイズ超なら一時ファイルを使用する
- バージョン 5.0 以前
- 一時ファイルを使用する
#
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
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
コマンドで確認出来ます。
echo "$(( $(ulimit -p) * 512 )) bytes"
4096 bytes
参考「ulimit - Bash Reference Manual」
バージョン 4.4
1. シェル変数 BASH_ARGV
および BASH_ARGC
サブルーチンは以下のものがあります:
- シェル関数
-
.
コマンドまたはsource
コマンドによって実行されるシェルスクリプト
サブルーチンにおける挙動が変化します:
- バージョン 5.0 以降
- 拡張デバッグモードを有効化時に初期化される: 位置パラメータ
- バージョン 4.4 以前
- 実行開始時に初期化される
- 拡張デバッグモードが無効の場合: 空
- 拡張デバッグモードが有効の場合: 位置パラメータ
- 実行開始時に初期化される
※初めから拡張デバッグモードを有効にする場合、内部的な処理方法は変わりますが見かけの挙動は変化しません。
#
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)
foo
1
0
foo
1 0
foo
1 0
バージョン 5.0 以降、メインルーチンにおける内部的な処理方法が変更されましたが、互換モードを使用しても内部的な処理方法は変化しません:
- バージョン 5.0 以降
- 拡張デバッグモードを有効化時に初期化される: 位置パラメータ
- 下位互換のため、拡張デバッグモードが無効の場合、参照時に動的に初期化される: 位置パラメータ
- バージョン 4.4 以前
- 実行開始時に初期化される: 位置パラメータ
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
foo
1
foo
1
参考「variables.c - bash.git - bash」(バージョン 5.0)
参考「NEWS - bash.git - bash」(バージョン 5.0)
参考「changelog\CWRU - bash.git - bash」(バージョン 5.0)
以下の場合に取得した値が正しくないことがあります:
- 途中で拡張デバッグモードを有効化する場合
- 拡張デバッグモードが無効のままの場合
#
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)
foo
1
0
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
foo
1
0
参考「BASH_ARGC - Bash Reference Manual」
参考「BASH_ARGV - Bash Reference Manual」
2. ループ内のサブシェルにおける break
または continue
コマンド
ループは以下のものがあります:
-
for
コマンド -
while
コマンド -
until
コマンド -
select
コマンド
違い:
- バージョン 5.0 以降
- 何もしない
- ループ外でコマンドを実行するのと同じ扱い
- バージョン 4.4 以前
- サブシェルを終了する
-
break
コマンドはループを終了しない -
continue
コマンドは次のループを開始しない
※バージョン 4.4 以前でも本来の正しいコマンドの挙動にならないため、そもそもサブシェル内で break
または continue
コマンドを使用しないべきです。
#
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
foo _ bar _ baz _
_ _ _
foo _ bar _ baz _
_ _ _
_ _ _
3. 関数内の export
および readonly
コマンドの前にある変数代入
3.1. 概要
関数呼び出しの前に変数代入がある場合の違い:
- バージョン 5.0 以降
- POSIX モードが無効の場合、シェル環境に影響しない
- POSIX モードが有効の場合、シェル環境に影響する
- バージョン 4.4 以前
- シェル環境に影響する
※関数呼び出しの前に変数代入がない場合、(POSIX モードかどうかやバージョンに関わらず) シェル環境に影響します。
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")
foo
baz
foo
baz
3.2. 詳細
コマンドの前にある変数代入はコマンドが参照する環境に影響し、基本的にシェル環境に影響しません。
(x=foo; x=bar bash -c 'echo "$x"'; echo "$x")
bar
foo
参考「3.7.1 Simple Command Expansion - Bash Reference Manual」
参考「3.7.4 Environment - Bash Reference Manual」
POSIX モードが無効の場合、export
および readonly
コマンドの前にある変数代入も基本的にシェル環境に影響しません。
ただし、それらのコマンドの引数で変数を指定する場合、コマンド自体がシェル環境に影響するためコマンドの前にある変数代入はシェル環境に影響します。
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")
foo
bar
foo
bar
シェル関数の環境は以下のような挙動をします:
- 基本的に呼び出し元の環境と同じ
- 基本的に呼び出し元と変数およびその値が共有される
function f() {
x=bar
}
(x=foo; f; echo "$x")
bar
参考「3.3 Shell Functions - Bash Reference Manual」
関数呼び出しの前に変数代入がある場合は基本的にシェル環境に影響しません。
function f() {
x=bar
}
(x=foo; x=bar f; echo "$x")
foo
参考「changelog\CWRU - bash.git - bash」(バージョン 5.0)
参考「changelog\CWRU - bash.git - bash」(バージョン 5.1)
参考「variables.c - bash.git - bash」(バージョン 5.1)
参考「0000654: unclear behavior of in-line variable assignments preceding functions, special built-ins - Austin Group Defect Tracker」(POSIX 解釈 654)
関数内で export
および readonly
コマンドを実行すると、関数呼び出しの前に変数代入がある場合でも関数呼び出しのシェル環境に影響します。
#
function f() {
export x
}
(x=foo; x=bar f; echo "$x")
#
function g() {
readonly x
}
(x=foo; x=bar g; echo "$x")
bar
bar
POSIX モードが無効のとき、バージョン 5.0 以降、関数内の export
および readonly
コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合、一時環境に影響しますがシェル環境に影響しません。
POSIX モードが無効のとき、バージョン 4.4 以前、関数内の export
および readonly
コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合、シェル環境に影響します。
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")
foo
baz
foo
baz
3.3. おまけ: POSIX モードが有効の場合
POSIX モードが有効の場合、特殊組み込みコマンドの前にある変数代入はシェル環境に影響します。
特殊組み込みコマンド:
:
.
break
continue
eval
exec
exit
export
readonly
return
set
shift
trap
unset
set -o posix
(x=foo; x=bar :; echo "$x")
set +o posix
(x=foo; x=bar :; echo "$x")
bar
foo
参考「4.4 Special Builtins - Bash Reference Manual」
POSIX モードが有効の場合、export
および readonly
コマンドも特殊組み込みコマンドのため、それらのコマンドの前にある変数代入はシェル環境に影響します。
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")
bar
bar
bar
bar
POSIX モードが有効のとき、関数内の export
および readonly
コマンドの前にある変数代入は、関数呼び出しの前に変数代入がある場合、シェル環境に影響します。
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")
baz
baz
バージョン 4.3
1. 引用符で囲まれている配列の複合代入
違い:
- バージョン 4.4 以降
- 警告を表示する
declare name[subscript]='(value1 value2 ... )'
- 警告を表示する
- バージョン 4.3 以前
- 警告を表示しない
※本記事ではインデックス配列の例を記載しますが、連想配列も同様の扱いになります。
#
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
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_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)
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
を設定する - 関数内でローカル変数を作成する
#
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
BASH_COMPAT=4.4
BASH_COMPAT=4.3
参考「changelog\CWRU - bash.git - bash」(バージョン 4.4)
バージョン 4.4 以降、関数内でローカル変数を作成する場合に警告が表示されない理由は以下の通りです:
-
declare name[subscript]
とすると (配列属性-a
または-A
を設定しなくても) 配列変数が作成される -
declare
コマンドでは以下の順で処理する- ローカル配列変数を作成する
- 配列変数の存在有無を確認する
- グローバル配列変数を作成する
- 配列変数が存在しない等の条件を満たす場合に警告を表示する
参考「declare.def\builtins - bash.git - bash」(バージョン 4.4)
2. パラメータ展開における一部の "bad substitution" エラー
違い:
- バージョン 4.4 以降
- POSIX モードが無効の場合、致命的でないエラー
- POSIX モードが有効の場合、致命的なエラー
- バージョン 4.3 以前
- 致命的でないエラー
参考「changelog\CWRU - bash.git - bash」(バージョン 4.4)
参考「Bash does not exit on non-interactive "Bad substitution" errors」
set +e -o posix
export SHELLOPTS
export BASH_COMPAT=4.4
bash -c $': "${}"\necho foo' || :
export BASH_COMPAT=4.3
bash -c $': "${}"\necho foo' || :
bash: line 1: ${}: bad substitution
bash: line 1: ${}: bad substitution
foo
全ての "bad substitution" エラーには影響しません。
参考「subst.c - bash.git - bash」(バージョン 4.4)
3. 関数内の break
および continue
コマンド
ループは以下のものがあります:
-
for
コマンド -
while
コマンド -
until
コマンド -
select
コマンド
ループ内に関数呼び出しがある場合の違い:
- バージョン 4.4 以降
- ループに影響しない
- バージョン 4.3 以前
- ループに影響する
#
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
foo bar baz
foo bar baz
バージョン 4.2
1. 二重引用符で囲まれたパターン置換 ${parameter/pattern/string}
における置換文字列 string
違い:
- バージョン 4.3 以降
- 引用符の削除を行う
- バージョン 4.2 以前
- 引用符の削除を行わない
#
function f() {
local -r x=foo
echo "${x/foo/'bar'}"
}
BASH_COMPAT=4.3
f
BASH_COMPAT=4.2
f
bar
'bar'
二重引用符内だけでなく、ヒアドキュメント内も影響します。
#
function f() {
local -r x=foo
cat <<-EOF
${x/foo/'bar'}
EOF
}
BASH_COMPAT=4.3
f
BASH_COMPAT=4.2
f
bar
'bar'
参考「subst.c - bash.git - bash」(バージョン 4.3)
2. 二重引用符で囲まれたパラメータ展開 ${...}
内の文字列
影響する場所は以下の表の通りです。
説明 | パラメータ展開 | 文字列 |
---|---|---|
デフォルト値 | ${parameter:-word} |
word |
デフォルト値代入 | ${parameter:=word} |
word |
エラー表示 | ${parameter:?word} |
word |
代替値 | ${parameter:+word} |
word |
違い:
- バージョン 4.3 以降
- POSIX モードが無効の場合
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 解析時に一重引用符の対応の確認を行わない
- POSIX モードが有効の場合、一重引用符は特別でない
- POSIX モードが無効の場合
- バージョン 4.2
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 解析時に一重引用符の対応の確認を行わない
- バージョン 4.1 以前
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
- ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない
パターン置換 ${parameter/pattern/string}
の置換文字列 string
では、(POSIX モードかどうかやバージョンに関わらず) 以下の挙動になります:
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
- ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない
※パターンには影響しません。
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
foo''bar}
foo''bar}
bash: line 1: x: foo
foo''bar}
foo'}'bar
foo'}'bar
bash: line 1: x: foo}bar
foo'}'bar
二重引用符内だけでなく、ヒアドキュメント内も影響します。
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
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」(バージョン 4.3)
参考「0000221: poor wording about even quotes in double quoted parameter expansion - Austin Group Defect Tracker」(POSIX 解釈 221)
参考「2.2.3 Double-Quotes - Shell Command Language」(2016 年版)
バージョン 4.1
1. オプションが与えられている time
違い:
- バージョン 4.2 以降
- POSIX モードが無効の場合、予約語
- POSIX モードが有効の場合、ディスクコマンド
- バージョン 4.1 以前
- 予約語
※バージョン 4.2 以降、POSIX モードが有効の場合はオプション -p
が与えられた場合でも予約語でなくディスクコマンドとみなされるため注意が必要です。
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'
disk command
disk command
reserved word
reserved word
ディスクコマンも予約語も、オプション -p
が与えられた場合の出力形式は同じです。
env time -p bash -c ''
set +o posix
time -p ! bash -c 'exit 1'
real 0.00
user 0.00
sys 0.00
real 0.00
user 0.00
sys 0.00
Bash の time
は組み込みコマンドでなく予約語です。
type -a echo
type -a time
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」
2. 二重引用符で囲まれたパラメータ展開 ${...}
内の文字列
影響する場所は以下の表の通りです。
説明 | パラメータ展開 | 文字列 |
---|---|---|
デフォルト値 | ${parameter:-word} |
word |
デフォルト値代入 | ${parameter:=word} |
word |
エラー表示 | ${parameter:?word} |
word |
代替値 | ${parameter:+word} |
word |
違い:
- バージョン 4.3 以降
- POSIX モードが無効の場合
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 解析時に一重引用符の対応の確認を行わない
- POSIX モードが有効の場合、一重引用符は特別でない
- POSIX モードが無効の場合
- バージョン 4.2
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 解析時に一重引用符の対応の確認を行わない
- バージョン 4.1 以前
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
- ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない
パターン置換 ${parameter/pattern/string}
の置換文字列 string
では、(POSIX モードかどうかやバージョンに関わらず) 以下の挙動になります:
- 一重引用符で囲まれた範囲を異なる文脈で展開する
- 二重引用符で囲まれている場合、解析時に一重引用符の対応の確認を行う
- ヒアドキュメント内の場合、解析時に一重引用符の対応の確認を行わない
※パターンには影響しません。
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
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 `''
解析時の挙動変更のため、未実行であってもエラーが発生します。
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
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」(バージョン 4.3)
バージョン 4.0
1. 条件コマンド [[ expression ]]
の <
および >
演算子
違い:
- バージョン 4.1 以降
- ロケールを考慮して文字列比較する
- バージョン 4.0 以前
- ロケールを考慮せず ASCII 順序で文字列比較する
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'
a A
a A
A a
A a
A a
バージョン 3.2
1. 対話モードでサブシェルでないコマンドリスト
コマンドリスト内のコマンドをシグナル SIGINT
で終了したときの違い:
- バージョン 4.0 以降
- コマンドリスト全体の実行を中断する
- バージョン 3.2 以前
- コマンドリスト全体の実行を中断しない
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' || :
foo
[1] 19092
[2] 19093
sleep 5
foo
[1] 19104
[2] 19105
sleep 5
bar
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' || :
foo
^C
foo
^C
bar
参考「jobs.c - bash.git - bash」(バージョン 4.0)
バージョン 3.1
1. 条件コマンド [[ expression ]]
の =~
演算子の右辺
違い:
- バージョン 3.2 以降
- クォートされた範囲は正規表現でなく文字列とみなす
- バージョン 3.1 以前
- クォートされても効果なし
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'
NG
OK
OK
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'
OK
NG
NG