bash-scope



たまにbashスクリプトを書くと忘れてしまってるので、bashの変数のスコープについてのメモ。

  1. 通常の変数はシェル変数と呼ばれ、関数外でも関数内でも宣言可
    1. シェル変数のスコープはグローバル変数となり、同スクリプト内(同プロセス内)のどこからでも参照・変更できる
      • 関数内でdeclareを使って変数を宣言する場合、グローバルにするには -g オプションが必要
      • 別のスクリプトファイルであっても、sourceコマンドで読み込んだ(呼び出した)場合は、そのプロセスは呼び出し元のスクリプト内に記述された事と同等の扱いとなる
    2. サブシェル として呼び出した場合、サブシェル内で同変数の参照はできるが、変更しても呼び出し元の変数には影響を与えない
      • 呼び出し時にパイプを使用すると、サブシェル内で実行されるので注意
    3. 別のスクリプト(別プロセス)を呼び出した場合、呼び出した先のスクリプト内で呼び出し元のシェル変数は参照も変更もできない
  2. local宣言された変数はローカル変数と呼ばれ、関数内でのみ宣言可
    1. 関数内で宣言されたローカル変数は、その関数内および同スクリプト内の呼び出し先関数内で自由に参照・変更ができるが、呼び出し先にない関数からは参照も変更もできない
      • 「ローカル変数はその関数内でのみ有効」と説明する人もいるが、ローカル変数であっても、同関数内で呼び出した先の関数内では参照・変更できるため注意。bashではその関数内だけに完全に閉じた変数を宣言する機能は今(ver5)のところなさそう。
    2. サブシェルとして呼び出した場合、サブシェル内で同変数の参照はできるが、変更しても呼び出し元の変数には影響を与えない
      • 呼び出し時にパイプを使用すると、サブシェル内で実行されるので注意
    3. 別のスクリプト(別プロセス)を呼び出した場合、呼び出した先のスクリプト内で呼び出し元のローカル変数は参照も変更もできない
    4. 関数呼び出し元に同名の変数が宣言済みの場合、呼び出し元の同名の変数の値は参照も変更もできなくなる
      • ただしbash 5.0以降では、shopt -s localvar_inherit を設定すると呼び出し元の同名の変数の値が自動で引き継がれ参照できるようになる
  3. export宣言された変数は環境変数と呼ばれる
    1. シェル変数の場合は1-3動作が、ローカル変数の場合は2-3の動作が変わり、別のスクリプトを呼び出した場合、呼び出した先のスクリプト内で呼び出し元のシェル変数やローカル変数を参照できるようになる。ただし変更はできない
      • ただし配列や連想配列は、exportしても参照できない (exportできない)

以下のテストスクリプトを使って動作確認してみました。実行環境のbashのバージョンは5.1.3です。

bash-scope-test.sh

#!/bin/bash

primal_function() {
	declare -g -x export_global="primal value"
	local -x export_local="primal value"
	declare -g -x -a export_global_array=("primal" "value")
	local -x -a export_local_array=("primal" "value")
	declare -g global="primal value"
	declare -g -a global_array=("primal" "value")
	local local="primal value"
	local -a local_array=("primal" "value")

	echo "---- call function ----"
	called_function
	echo "---- call function (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call function as subshell ----"
	(called_function)
	echo "---- call function as subshell (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call function as pipe ----"
	called_function | cat
	echo "---- call function as pipe (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call other script ----"
	./other-script.sh
	echo "---- call other script (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call redefined function ----"
	called_redefined_function
	echo "---- call redefined function (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call redefined function as localvar_inherit ----"
	shopt -s localvar_inherit
	called_redefined_function
	echo "---- call redefined function as localvar_inherit (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	shopt -u localvar_inherit
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")	
}

# caelld from primal_function()
called_function() {
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	export_global="**called value**"
	export_local="**called value**"
	export_global_array=("**called" "value**")
	export_local_array=("**called" "value**")
	global="**called value**"
	global_array=("**called" "value**")
	local="**called value**"
	local_array=("**called" "value**")
}

# caelld from primal_function()
called_redefined_function() {
	# local export_global
	# local export_local
	# local -a export_global_array
	# local -a export_local_array
	# local global
	# local -a global_array
	# local local
	# local -a local_array

	declare -g -x export_global
	local -x export_local
	declare -g -x -a export_global_array
	local -x -a export_local_array
	declare -g global
	declare -g -a global_array
	local local
	local -a local_array

	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	export_global="**redefined value**"
	export_local="**redefined value**"
	export_global_array=("**redefined" "value**")
	export_local_array=("**redefined" "value**")
	global="**redefined value**"
	global_array=("**redefined" "value**")
	local="**redefined value**"
	local_array=("**redefined" "value**")
}

# called from main()
other_function() {
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	export_global="**other value**"
	export_local="**other value**"           # this is not declared, so it becomes global
	export_global_array=("**other" "value**")
	export_local_array=("**other" "value**") # this is not declared, so it becomes global
	global="**other value**"
	global_array=("**other" "value**")
	local="**other value**"                  # this is not declared, so it becomes global
	local_array=("**other" "value**")        # this is not declared, so it becomes global
}

primal_function

echo
echo "---- main() ----"
echo "       export_global=$export_global"
echo "        export_local=$export_local"
echo " export_global_array=${export_global_array[*]}"
echo "  export_local_array=${export_local_array[*]}"
echo "              global=$global"
echo "        global_array=${global_array[*]}"
echo "               local=$local"
echo "         local_array=${local_array[*]}"

echo "---- call other function in main() ----"
other_function

echo "---- call other function in main() (returned) ----"
echo "       export_global=$export_global"
echo "        export_local=$export_local"
echo " export_global_array=${export_global_array[*]}"
echo "  export_local_array=${export_local_array[*]}"
echo "              global=$global"
echo "        global_array=${global_array[*]}"
echo "               local=$local"
echo "         local_array=${local_array[*]}"

other-script.sh

#!/bin/bash

echo "       export_global=$export_global"
echo "        export_local=$export_local"
echo " export_global_array=${export_global_array[*]}"
echo "  export_local_array=${export_local_array[*]}"
echo "              global=$global"
echo "        global_array=${global_array[*]}"
echo "               local=$local"
echo "         local_array=${local_array[*]}"

export_global="**other script value**"
export_local="**other script value**"
export_global_array=("**other" "script value**")
export_local_array=("**other" "script value**")
global="**other script value**"
global_array=("**other" "script value**")
local="**other script value**"
local_array=("**other" "script value**")

実行結果

$ ./bash-scope-test.sh
---- call function ---- ★通常の関数呼び出しは全部参照可能
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call function (returned) ---- ★通常の関数呼び出し先では呼び出し元を変更可
export_global=**called value**
export_local=**called value**
export_global_array=**called value**
export_local_array=**called value**
global=**called value**
global_array=**called value**
local=**called value**
local_array=**called value**

---- call function as subshell ---- ★サブシェルでの呼び出しは全部参照可能
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call function as subshell (returned) ---- ★サブシェルでの呼び出し先では呼び出し元を変更不可
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value

---- call function as pipe ---- ★パイプでの呼び出しはサブシェルと同等
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call function as pipe (returned) ----
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value

---- call other script ---- ★他のスクリプト呼び出しではexportされた変数しか参照できない
export_global=primal value
export_local=primal value
export_global_array= ★配列はexportできない
export_local_array= ★配列はexportできない
global=
global_array=
local=
local_array=
---- call other script (returned) ---- ★他のスクリプト内で呼び出し元を変更不可
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value

---- call redefined function ----
export_global=primal value ★ 呼び出し先で同名のグローバル変数の定義は、呼び出し元の変数がそのまま参照される
export_local= ★ 全く異なる関数内で定義されたローカル変数は参照できない
export_global_array=primal value
export_local_array=
global=primal value
global_array=primal value
local=
local_array=
---- call redefined function (returned) ----
export_global=**redefined value** ★ 呼び出し先で同名のグローバル変数の定義は、呼び出し元の変数がそのまま変更できる
export_local=primal value
export_global_array=**redefined value**
export_local_array=primal value
global=**redefined value**
global_array=**redefined value**
local=primal value
local_array=primal value

---- call redefined function as localvar_inherit ----
export_global=primal value ★localvar_inheritは、グローバルな変数の動作には影響なし
export_local=primal value ★localvar_inheritは、local宣言された変数に呼び出し元の同名の変数の値が代入される
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call redefined function as localvar_inherit (returned) ----
export_global=**redefined value**
export_local=primal value ★localvar_inheritでも、local宣言された変数は呼び出しもとの同名の変数の値は変更されない
export_global_array=**redefined value**
export_local_array=primal value
global=**redefined value**
global_array=**redefined value**
local=primal value
local_array=primal value

---- main() ----
export_global=primal value ★関数内で定義されたグローバル変数はスクリプト内ではどこからでも参照可能
export_local=
export_global_array=primal value
export_local_array=
global=primal value
global_array=primal value
local=
local_array=
---- call other function in main() ----
export_global=primal value ★関数内で定義されたグローバル変数はスクリプト内ではどこからでも参照可能
export_local=
export_global_array=primal value
export_local_array=
global=primal value
global_array=primal value
local=
local_array=
---- call other function in main() (returned) ----
export_global=**other value**
export_local=**other value** ★other_function()内ではprimal_function()内のlocal変数は参照できないため、直接変数を変更するとグローバルとみなされてしまう
export_global_array=**other value**
export_local_array=**other value**
global=**other value**
global_array=**other value**
local=**other value**
local_array=**other value**

なお関数にlocal宣言はありません。そのため変数と異なり関数内で関数を定義した場合、関数のスコープは全てグローバルとなります。関数はexportでき、変数と同様に呼び出し先のスクリプト内でも使用できます。