function
まず、キーワード vs.を使用して関数を宣言するときの bash と ksh の一般的なスコープの違い (動的/静的) を認識していmyfunction()
ます。この投稿では、読み取り専用変数に関するスコープの問題についてのみ説明します。
しかし、今日、私は私を混乱させる何かに出くわしました。キーワードで宣言された関数内からファイルを取得するスクリプトがありますfunction
(そのため、「グローバル スコープのゴーグル」を通してこれらの個別のファイルを見たので、次のことが起こった理由がすぐにはわかりませんでした)。最近のクリーンアップの一環として、これらのソース ファイル内のさまざまな変数を読み取り専用にしましたが、変数を読み取り専用としてマークした方法によっては、コードの一部が ksh93 で動作しなくなったことに気付きました。より具体的には、 を使用した場合、ソース ファイルの残りの部分では設定が解除されますreadonly FOO=bar
。${FOO}
これは問題を示しています:
(注: 動作はインライン コードと同じですが (vs. ソースを取得する 2 番目のスクリプト)、ここでいくつかの行を保存し、投稿がかなり長いので、そのままにしておきます)
readonly_test_sourced.sh:
readonly foo=function
typeset -r bar=function
typeset baz=function
readonly baz
qux=function
readonly qux
quux=function
typeset -r quux
readonly_test.sh:
function f
{
. ./readonly_test_sourced.sh
printf "foo=${foo}\nbar=${bar}\nbaz=${baz}\nqux=${qux}\nquux=${quux}\n"
}
g()
{
. ./readonly_test_sourced.sh
printf "foo=${foo}\nbar=${bar}\nbaz=${baz}\nqux=${qux}\nquux=${quux}\n"
}
[ -n "${KSH_VERSION}" ] && echo "ksh: ${KSH_VERSION}"
[ -n "${BASH_VERSION}" ] && echo "bash: ${BASH_VERSION}"
for var in foo bar baz qux quux; do
unset "${var}"
eval "$var=global" # don't do this at home, there are better ways
done
func="${1:-f}"
echo
echo "inside function ${func}"
echo '----------------'
${func}
echo
echo "global scope after calling ${func}"
echo '----------------------------'
printf "foo=${foo}\nbar=${bar}\nbaz=${baz}\nqux=${qux}\nquux=${quux}\n"
これは、ksh93u+ で得られる出力です。
$ ksh ./readonly_test.sh f
ksh: Version JM 93u+ 2012-02-29
inside function f
----------------
foo=
bar=function
baz=function
qux=
quux=
global scope after calling f
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
$ ksh ./readonly_test.sh g
ksh: Version JM 93u+ 2012-02-29
inside function g
----------------
foo=function
bar=function
baz=function
qux=function
quux=function
global scope after calling g
----------------------------
foo=function
bar=function
baz=function
qux=function
quux=function
これは私がbash 4.2で得たものです:
$ bash ./readonly_test.sh f
bash: 4.2.42(1)-release
inside function f
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling f
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
$ bash ./readonly_test.sh g
bash: 4.2.42(1)-release
inside function g
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling g
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
また、mksh (マンページに従って動的スコープを実装し、bash と同じ結果が得られます) で実行しました。
$ mksh ./readonly_test.sh f
ksh: @(#)MIRBSD KSH R40 2012/03/20
inside function f
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling f
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
$ mksh ./readonly_test.sh g
ksh: @(#)MIRBSD KSH R40 2012/03/20
inside function g
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling g
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
所見:
readonly
同義のtypeset -r
場合もあるが、そうでない場合もあるバッシュとmksh
3 つの異なるシナリオが考えられます (f と g の両方)。
- [1]グローバル変数に
readonly foo=function
代入し、新しいローカル変数を宣言しない ([4] と同じ)'function'
foo
- [2]新しいローカル変数に
typeset -r bar=function
割り当てます([3] と同じ)'function'
bar
- [3]新しいローカル変数に
typeset baz=function; readonly baz
割り当てます([2] と同じ)'function'
baz
- [4]グローバル変数に
qux=function; readonly qux
代入し、新しいローカル変数を宣言しません ([1] と同じ)。はローカル スコープとグローバル スコープの両方で読み取り専用ですが、これはグローバル変数を読み取り専用としてマークし、動的スコープにより関数内でも読み取り専用になるため、これは予期されることです (サイド ノート:も参照してください)。'function'
qux
qux
- [5]グローバル変数に
quux=function; typeset -r quux
代入し、値なしで新しいローカル変数を宣言します'function'
quux
quux
- [1]グローバル変数に
readonly
新しい(ローカル)変数を宣言することはありません(予想どおり)。[1] と [4] ではグローバル変数を読み取り専用としてマークし、[3] では新しいローカル変数をマークします。これは完全に私が期待するものでreadonly
あり、動作するスコープがそれぞれの変数自体と同じであることを意味します。x=y
;$x
がローカルの場合readonly x
、ローカル変数をx
読み取り専用としてマークしますx=y
;$x
がグローバルの場合readonly x
、グローバル変数をx
読み取り専用としてマークします
[1] / [2] と [4] / [5] の間の一貫性
ksh で (f について説明します。g は期待どおりに動作します):
- また、3 つの異なるシナリオが考えられます。
- [6]
readonly foo=function
: [5]、[8]、および [9] と同様ですが、これは単一のコマンドであるため、より混乱を招きます (代入が最初にreadonly
/typeset -r
後であるのとは対照的に)。どうやら値のないreadonly
新しいローカル変数を宣言してfoo
いますが、グローバル変数foo
を に設定しています'function'
。?!? .foo
関数とグローバルスコープの両方で読み取り専用になります。 - [7]新しいローカル変数に
typeset -r bar=function
割り当てます([8] と同じ)'function'
bar
- [8]新しいローカル変数に
typeset baz=function; readonly baz
割り当てます([7] と同じ)。関数スコープで読み取り専用になる'function'
baz
baz
- [9]グローバル変数に
qux=function; readonly qux
代入し、値なしで新しいローカル変数を宣言します ([5]、[6]、[10] と同じ)。関数スコープで読み取り専用になる'function'
qux
qux
qux
- [10]グローバル変数に
quux=function; typeset -r quux
代入し、値なしで新しいローカル変数を宣言します ([5]、[9]、[10] と同じ)。関数スコープで読み取り専用になります。'function'
quux
quux
quux
- [6]
readonly
[6] と [9] では新しいローカル変数を宣言しているようですが、[8] ではそうではありません。
- また、3 つの異なるシナリオが考えられます。
bash の動作が期待されます。typeset
(= declare
) 関数のスコープで作成/変更します (bash には、関数-g
内で使用された場合でもグローバル スコープで作成/変更を強制するオプションがあります)。readonly
実際には、既存の変数を「マーク」するだけで、新しいローカル変数を導入することはありません。[5] 以前は未設定の読み取り専用変数を宣言する必要がなかったので、少し戸惑いました。同じ名前のグローバル変数が存在する場合、それを変更すると想定していましたが、一貫しているため、この動作に完全に耐えることができます。他のシナリオとの存在-g
。
しかし、私の理解では、質問のそれぞれのセクションを読み直したときに何かを見逃していない限り、ksh のマニュアル ページでは、上記のシナリオのすべてと、キーワードreadonly
とキーワードの微妙な違いを完全に説明することはできません。typeset -r
私にとって最も紛らわしいのは、 と のスコープの違いの説明の近くにキーワードがどこにも言及されておらreadonly
ず、組み込みの短い説明でもこれについて何も言及されていないことです。それに基づいて、新しい、静的にスコープされた変数が導入されるとは決して思いませんし、一部の (すべてではない) シナリオで導入されることは本当に予想外のようです。foo()
function bar
readonly
readonly
typeset -r
私にとって最も紛らわしいシナリオは [6] であり、そこで何が起こっているのか正確には理解できません (これは私のコードを壊した特定のシナリオでもあります)。
私の観察は正しいですか、誰かがksh93の動作に光を当てることができますか? (この質問が許容範囲内であることを願っています(しゃれは意図されていません))