0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Bash] シェルオプション assoc_expand_once

Last updated at Posted at 2024-08-10

シェルオプション assoc_expand_once を有効にすると、一部の場所で連想配列の添字を 1 回のみ展開するようになります。

Bash バージョン 5.2 以降、一部の場所で 1 回のみ展開するようになりましたが、シェルオプション assoc_expand_once の挙動と異なります。

※シェルオプション assoc_expand_once は Bash バージョン 5.2 以降でも影響があります。

展開については以下を参照。

参考「3.5 Shell Expansions - Bash Reference Manual

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

1. 基本

Bash バージョン 5.2 現在、シェルオプション assoc_expand_once の影響を受ける場所は以下の通りです:

  • 算術式
    • let コマンド
  • 組み込みコマンドによる変数代入
    • printf コマンド
    • read コマンド
    • wait コマンド
  • 組み込みコマンドによる配列要素の操作
    • unset コマンド
    • test expr および [ expr ] の 条件式の演算子 -v の引数

※ Bash バージョン 5.2 で影響を受ける場所は、互換モードのバージョン 5.1 以前でも影響があります。

1.1. 算術式

1.1.1. let コマンド

# 
function f() {

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

	local -i y
	let 'y = assoc_array[$x]'

	echo "$y"

}

shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f

# 
function g() {

	local -Ai assoc_array=()
	local -r x='x'

	let 'assoc_array[$x] = 23'

	echo "${!assoc_array[@]}"

}

shopt -u assoc_expand_once
g
shopt -s assoc_expand_once
g
実行結果
23
42
x
$x

ちなみに、整数属性が設定された変数に対して代入文を用いて代入する場合は、シェルオプション assoc_expand_once の影響を受けません。

# 
function f() {

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

	local -ri y='assoc_array[$x]'

	echo "$y"

}

unset BASH_COMPAT
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f

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

BASH_COMPAT=5.0
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
23
23
23
23
23
23

1.2. 組み込みコマンドによる変数代入

1.2.1. printf コマンド

# 
function f() {

	local -A assoc_array=()
	local -r x='x'

	printf -v 'assoc_array[$x]' foo

	echo "${!assoc_array[@]}"

}

shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
x
$x

1.2.2. read コマンド

# 
function f() {

	local -A assoc_array=()
	local -r x='x'

	read 'assoc_array[$x]' <<< foo

	echo "${!assoc_array[@]}"

}

shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
x
$x

1.2.3. wait コマンド

# 
function f() {

	local -A assoc_array=()
	local -r x='x'

	: &
	wait -p 'assoc_array[$x]' $!

	echo "${!assoc_array[@]}"

}

shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
x
$x

1.3. 組み込みコマンドによる配列要素の操作

1.3.1. unset コマンド

# 
function f() {

	local -A assoc_array=([x]=foo ['$x']=bar)
	local -r x='x'

	unset 'assoc_array[$x]'

	echo "${!assoc_array[@]}"

}

shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
$x
x

1.3.2. test expr および [ expr ] の 条件式の演算子 -v の引数

# 
function f() {

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

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

}

shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
OK
OK
NG
NG

2. Bash バージョン 5.1 以前

Bash バージョン 5.1 以前、シェルオプション assoc_expand_once の影響を受ける場所は以下の通りです:

  • 算術式
    • 算術式展開 $(( expression ))
    • 算術コマンド (( expression ))
    • 算術 forfor (( expr1 ; expr2 ; expr3 ))
    • インデックス配列の添字
    • パラメータ展開の部分列 ${parameter:offset} および ${parameter:offset:length}
    • 条件コマンド [[ expression ]] の算術演算子の引数
  • 組み込みコマンドによる配列要素の操作
    • 条件コマンド [[ expression ]] の条件式の演算子 -v の引数

※ Bash バージョン 5.2 で影響を受ける場所は、互換モードのバージョン 5.1 以前でも影響があります。

2.1. 算術式

2.1.1. 算術式展開 $(( expression ))

# 
function f() {

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

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

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

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
23
23
42
42

2.1.2. 算術コマンド (( expression ))

# 
function f() {

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

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

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

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
OK
OK
NG
NG

2.1.3. 算術 forfor (( expr1 ; expr2 ; expr3 ))

# 
function f() {

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

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

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

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
23
23
42
42

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

# 
function f() {

	local -a indexed_array=(foo bar)

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

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

	echo "${indexed_array[@]}"

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

	echo "${indexed_array[@]}"

}

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

# 
function g() {

	local -a indexed_array=(foo bar)

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

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

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

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
g
shopt -s assoc_expand_once
g
実行結果
baz bar
baz bar
foo baz
foo baz
foo
foo
bar
bar

2.1.5. パラメータ展開の部分列 ${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 offset='assoc_array[$x]'
	echo "${parameter:offset}"

}

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

# 
function g() {

	local -r parameter=foo

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

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

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
g
shopt -s assoc_expand_once
g
実行結果
foo
oo
foo
fo

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

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

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

	[[ 'assoc_array[$x]' -ne 0 ]] && echo 'OK' || echo 'NG'

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
OK
NG

2.2. 組み込みコマンドによる配列要素の操作

2.2.1. 条件コマンド [[ expression ]] の条件式の演算子 -v の引数

# 
function f() {

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

	[[ -v 'assoc_array[$x]' ]] && echo 'OK' || echo 'NG'

}

BASH_COMPAT=5.1
shopt -u assoc_expand_once
f
shopt -s assoc_expand_once
f
実行結果
OK
NG

3. 参考

3.1. シェルオプション

参考「4.3.2 The Shopt Builtin - Bash Reference Manual

シェルオプション assoc_expand_once が有効の場合、連想配列の添字の多重評価を抑制します:

  • 算術式
  • 組み込みコマンドによる変数代入
  • 組み込みコマンドによる配列の参照先の取得

3.2. NEWS

参考「NEWS - bash.git - bash」(バージョン 5.2)

  • bash-5.2
    • シェルオプション assoc_expand_once が設定されていない場合でも、組み込みコマンド unset は添字の解析や展開を行わずに引数を配列の添字として扱おうとする
    • シェルオプション assoc_expand_once が設定されている場合、一部の組み込みコマンドは配列添字を解析しない
      • printf
      • test
      • read
      • wait
  • bash-5.0
    • 連想配列の添字を 1 回だけ展開しようとする新しいシェルオプション assoc_expand_once

3.3. COMPAT

参考「COMPAT - bash.git - bash」(バージョン 5.2)

以前のバージョンとの互換性:

  • Bash-5.2 では、あたかもシェル オプション assoc_expand_once が設定されているかのように動作することにより、特定の状況下、特に算術評価における配列添字の二重展開を防止しようとする

互換性レベル:

  • バージョン 5.1
    • 条件コマンド [[ の演算子 (例: [[ -v) の引数として使用されるインデックス配列および連想配列の添字は複数回展開する
      • Bash-5.2 では、assoc_expand_once オプションが有効になっているかのように動作する

3.4. CHANGES

参考「CHANGES - bash.git - bash」(バージョン 5.2)

  • bash-5.2-alpha
    • シェルオプション assoc_expand_once が設定されていない場合でも、組み込みコマンド unset は添字の解析や展開を行わずに引数を配列の添字として扱おうとする
    • シェルオプション assoc_expand_once が設定されている場合、一部の組み込みコマンドは配列添字を解析しない
      • printf
      • test
      • read
      • wait
  • bash-5.1-rc3
    • シェルオプション assoc_expand_once は条件式の演算子 -v の引数および条件コマンド [[ の評価に影響する
  • bash-5.0-alpha
    • 連想配列の添字を 1 回だけ展開しようとする新しいシェルオプション assoc_expand_once

3.5. 互換モード

参考「6.12 Shell Compatibility Mode - Bash Reference Manual

互換性レベル:

  • バージョン 5.1
    • 算術式内の展開を複数回行う
      • 算術コマンド (( expression ))
      • 算術 forfor (( expr1 ; expr2 ; expr3 ))
      • 条件コマンド [[ expression ]] の算術演算子の引数
      • パラメータ展開の部分列 ${parameter:offset} および ${parameter:offset:length}
      • 算術式展開 $(( expression ))
      • インデックス配列の添字

3.6. Changelog

3.6.1. バージョン 5.0

参考「changelog\CWRU - bash.git - bash」(バージョン 5.0)

  • 2016-12-12
    • arrayfunc.c
      • assoc_expand_once: 新しい変数
    • arrayfunc.h
      • assoc_expand_once
    • builtins/set.def
      • unset_builtin: assoc_expand_once
    • builtins/shopt.def
      • assoc_expand_once: 新しいシェルオプション
    • expr.c
      • assoc_expand_once
  • 2016-12-14
    • expr.c
      • expr_bind_variable: assoc_expand_once
  • 2016-12-15
    • builtins/read.def
      • read_builtin: assoc_expand_once
      • bind_read_variable: assoc_expand_once
    • builtins/printf.def
      • printf_builtin: assoc_expand_once
      • bind_printf_variable: assoc_expand_once
  • 2018-01-31
    • doc/{bash.1,bashref.texi}
      • shopt: assoc_expand_once
  • 2018-09-23
    • arrayfunc.c
      • assign_array_element: assoc_expand_once
  • 2018-09-29
    • arrayfunc.c
      • valid_array_reference: assoc_expand_once
  • 2018-10-16
    • expr.c
      • expr_skipsubscript: assoc_expand_once

3.6.2. バージョン 5.1

参考「changelog\CWRU - bash.git - bash」(バージョン 5.1)

  • 2018-11-10
    • test.c
      • unary_test: assoc_expand_once
        • [[
        • [

3.6.3. バージョン 5.2

参考「changelog\CWRU - bash.git - bash」(バージョン 5.2)

  • 2021-05-06
    • execute_cmd.c
      • execute_cond_command: -v unary_test assoc_expand_once
  • 2021-05-07
    • builtins/common.[ch]
      • set_expand_once: array_expand_once
    • execute_cmd.c
      • execute_cond_node: -v unary_test set_expand_once array_expand_once
  • 2021-05-09
    • builtins/shopt.def
      • expand_once_flag: assoc_expand_once set_assoc_expand
      • set_assoc_expand: expand_once_flag assoc_expand_once
    • builtins/common.h
      • expand_once_flag
  • 2021-05-11
    • test.c
      • unary_test: -v valid_array_reference assoc_expand_once
  • 2021-11-16
    • builtins/common.c
      • set_expand_once: -v assoc_expand_once
        • [[
  • 2021-12-29
    • builtins/common.h
      • SET_VFLAGS: assoc_expand_once
        • vflags: valid_array_reference
        • bindflags: assign_array_element bind_int_variable
    • builtins/printf.def
      • printf_builtin: SET_VFLAGS assoc_expand_once
    • builtins/read.def
      • read_builtin: SET_VFLAGS assoc_expand_once
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?