10

関数内の単一のステートメントから配列を宣言してエクスポートすることは可能ですか?

私の現在の回避策は、最初に宣言してからエクスポートすることです。

f() { foo=(1 2 3); export foo; }; f; export -p | grep foo=
declare -ax foo='([0]="1" [1]="2" [2]="3")'

私はそれを観察します:

f() { export bar=(1 2 3); }; f; export -p | grep bar=
<no output>

と:

f() { export baz="(1 2 3)"; }; f; export -p | grep baz=
declare -x baz="(1 2 3)" # not an array

v3.2.48(1)-release を使用していますが、アップグレードできません。


背景:

「いくつかの」Djangoを一緒に勉強しようとしている友人がいます。

彼はコマンド ラインに関しては私より無知で、OSX hackintosh では次のものが必要です。

  • 対話型シェルを起動する
  • 私の仕様に従って、django bin dirを含むPATH変数を見つけます
  • さまざまな django ライブラリが表示された、更新された PYTHONPATH 環境変数を見つけます
  • ダブルクリック後にコマンドの入力を開始するための優れたインタラクティブな ipython シェル
  • (トリッキー) CTRL-D で ipython を終了すると、フォールバックする対話型シェル

Windows では、cmd.exe などのコマンド ショートカットにエイリアスを設定し、カスタム環境変数を設定して ipython を起動します。これは機能します。ipython を終了した後でも、コマンド インタープリターで自分自身を見つけることができます。

これは、OSX の標準の bash でまったく可能ですか? 私は bash -c でプレイしましたが、ディレクトリに変更したり、python を終了してターミナルにとどまることができるなど、多くのことが機能しません。- s でもプレイしました。

4

3 に答える 3

9

Bash (またはその他のシェル) で配列をエクスポートすることはできません。Bash は配列を環境にエクスポートしませ(いつか実装されるまでbugsは、マンページのセクションを参照してください)。

ここでいくつかのことが起こっています。繰り返し-xますが、配列に属性を設定することは、実際にはエクスポートされないため無意味です。これらの結果が表示される唯一の理由は、配列をローカライズする前に定義したために、その名前がローカル (またはグローバル スコープ) にされた次の最も外側のスコープにドロップダウンするためです。

したがって、明確にするために、宣言コマンドを使用するときはいつでも、exportまたはreadonly配列以外の代入を引数として使用する場合を除いて、常にローカルを作成しています。これは、ローカルまたは配列のいずれも指定しない POSIX であるためです。

function f {
    typeset -a a # "a" is now local to "f"
    g
    printf 'Now back in "f": %s\n' "$(typeset -p a)"
}

function g {
    a=(1 2 3)                               # Assigning f's localized "a"
    typeset -a a                            # A new local a
    printf 'In "g": %s\n' "$(typeset -p a)" # g's local is now empty.
    a=(a b c)                               # Now set g's local to a new value.
    printf 'Still in "g": %s\n' "$(typeset -p a)"
}

f

# In "g": declare -a a='()'
# Still in "g": declare -a a='([0]="a" [1]="b" [2]="c")'
# Now back in "f": declare -a a='([0]="1" [1]="2" [2]="3")'

配列を引数として与えるとexport、Bashは他の宣言コマンドと同様にローカルにします。export基本的に配列の定義には使用しないでください。exportは POSIX ビルトインであり、その動作は配列に対して定義されていません。これがおそらく、bash がtypeset -ax a=(1 2 3). typesetlocal、またはを使用しdeclareます。何が「正しい」かを知る方法はなく、他のシェルと比較することさえできません。ksh93 は、宣言コマンドへの引数として配列の代入を受け入れる唯一の他のものであり、その動作は Bash と一致しません。

理解しておくべき重要なことは、環境はそれとは何の関係もないということです。POSIX のみのコマンドで非標準的なことをしようとすると、ローカルの癖で遊んでいるだけです。

必要な効果を実際に得るために、 を使用できますtypeset -p

function f {
    typeset -a "${1}=(1 2 3)"
    typeset -p "$1"
}

typeset -a arr
eval "$(f arr)"
typeset -p arr

Bash は正しい結果が得られることを保証しますが、これはあまり有用ではなく、このアプローチを使用することはめったにありません。通常は、呼び出し元にローカルを定義させ、動的スコープを使用して作業を行わせる方が良いでしょう (すでに発見したように... エクスポートせずに行うだけです)。

Bash 4.3typeset -nでは、これを処理する最も正しい方法である を使用できます。

于 2013-04-06T20:00:38.450 に答える
3

最初のオプションが機能する唯一のオプションのようです。bash私は4.2 と 3.2.48を試しました。私にとって興味深い情報は、あなたの例のこれらのマイナーな変形でした:

$ f() { declare -a bar=(1 2 3); export bar; export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ f() { export  bar=(1 2 3); export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ f() { bar=(1 2 3); export bar; export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ unset bar
$ f() { bar=(1 2 3); }; f; set | grep bar=
bar=([0]="1" [1]="2" [2]="3")
    bar=(1 2 3)
$

exportこれらの例では、関数の内側と外側をテストします。変数は関数内で定義されているため、スコープが関数に限定されているように見えます。例外は、属性が適用される前に変数が定義されている場合 (最後の 2 つの関数) です。グローバル変数が作成され、エクスポートされます (1 つのケースでは)。

したがって、関数からエクスポートされた配列を取得する場合は、declareorexportステートメントを使用せずに作成し (変数を関数に対してローカルにするため)、エクスポートする必要があります。

それが説明できることを願っています。何が起こっているのかぼんやりと見えて、なんとなく納得できます。私はそれを説明する必要があるだけでなく、私がそれを説明したかどうか確信が持てません。


マニュアルのdeclareセクションでは、次のように述べています。bash

関数で使用するdeclareと、コマンドと同様に、それぞれの名前をローカルにしlocalます。

に同等の文言はありませんexport。ただし、観察された動作は、によって実装されているか のようです。exportdeclare -x

于 2013-04-05T04:46:11.130 に答える