1

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'quxqux
      • [5]グローバル変数にquux=function; typeset -r quux代入し、値なしで新しいローカル変数を宣言します'function'quuxquux
    • 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'bazbaz
      • [9]グローバル変数にqux=function; readonly qux代入し、値なしで新しいローカル変数を宣言します ([5]、[6]、[10] と同じ)。関数スコープで読み取り専用になる'function'quxquxqux
      • [10]グローバル変数にquux=function; typeset -r quux代入し、値なしで新しいローカル変数を宣言します ([5]、[9]、[10] と同じ)。関数スコープで読み取り専用になります。'function'quuxquuxquux
    • readonly[6] と [9] では新しいローカル変数を宣言しているようですが、[8] ではそうではありません。

bash の動作が期待されます。typeset(= declare) 関数のスコープで作成/変更します (bash には、関数-g内で使用された場合でもグローバル スコープで作成/変更を強制するオプションがあります)。readonly実際には、既存の変数を「マーク」するだけで、新しいローカル変数を導入することはありません。[5] 以前は未設定の読み取り専用変数を宣言する必要がなかったので、少し戸惑いました。同じ名前のグローバル変数が存在する場合、それを変更すると想定していましたが、一貫しているため、この動作に完全に耐えることができます。他のシナリオとの存在-g

しかし、私の理解では、質問のそれぞれのセクションを読み直したときに何かを見逃していない限り、ksh のマニュアル ページでは、上記のシナリオのすべてと、キーワードreadonlyとキーワードの微妙な違いを完全に説明することはできません。typeset -r私にとって最も紛らわしいのは、 と のスコープの違いの説明の近くにキーワードがどこにも言及されておらreadonlyず、組み込みの短い説明でもこれについて何も言及されていないことです。それに基づいて、新しい、静的にスコープされた変数が導入されるとは決して思いませんし、一部の (すべてではない) シナリオで導入されることは本当に予想外のようです。foo()function barreadonlyreadonlytypeset -r

私にとって最も紛らわしいシナリオは [6] であり、そこで何が起こっているのか正確には理解できません (これは私のコードを壊した特定のシナリオでもあります)。

私の観察は正しいですか、誰かがksh93の動作に光を当てることができますか? (この質問が許容範囲内であることを願っています(しゃれは意図されていません))

4

2 に答える 2

1

間違いは次のとおりです。関数を使用し"local"ないか、サブシェルにしない場合、関数の「ローカル」スコープはありません。

したがって、f() ( <code> )代わりに使用f() { <code> }してローカルスコープを取得してください!

それでも、あなたは1つのポイントを持っています!

"readonly <var>""declare -r <var>"!の違い

# ./readonly_test.sh
bash: 3.00.16(1)-release

inside function
----------------
foo=function
bar=function
baz=function
qux=function
quux=

global scope after calling
----------------------------
foo=global
bar=global
baz=global
qux=global
quux=global
于 2013-08-22T12:55:25.560 に答える
1

シェルの非互換性の世界へようこそ :)

私が質問を正しく理解していれば、それは次の違いについてです

関数何とか { }

何とか(){}

ad man ksh(1) を見てください (Solaris で違いがあれば)

 Functions defined by the function name syntax and called  by
 name execute in the same process as the caller and share all
 files and present working directory with the  caller.
 ...
 Ordinarily,  variables
 are  shared  between  the  calling program and the function.
 However, the typeset special built-in command used within  a
 function  defines  local  variables whose scope includes the
 current function. They can be passed to functions that  they

 Functions defined  with  the  name()  syntax  and  functions
 defined  with the function name syntax that are invoked with
 the . special built-in are executed in the caller's environ-
 ment  and  share  all  variables  and traps with the caller.
 Errors within these function  executions  cause  the  script
 that contains them to abort.
于 2013-05-06T13:52:40.597 に答える