変数の割り当ては現在実行中のシェルに影響します


8

いくつかのコードを書いているとき、私はこの行を見つけました:

$ TZ="America/Los_Angeles"       date; echo "$TZ"
Thu Dec 24 14:39:15 PST 2015

「ロサンゼルス」での実際の時間を正しく示し、変数の値がTZ保持されないこと。期待どおりすべて。

ただし、この行では、一部の形式を日付に展開し、基本的に同じことを実行するため、TZの値が保持されます。

TZ="America/Los_Angeles" eval  date; echo "$TZ"
Thu Dec 24 14:41:34 PST 2015
America/Los_Angeles

さらにいくつかのテストを行った結果、これが一部のシェルでのみ発生することがわかりました。ダッシュ、kshでは発生しますが、bashやzshでは発生しません。

Q

質問は次のとおりです。

  • TZの値が現在のシェルで保持されているのはなぜですか?
  • それをどのように回避/制御できますか(可能な場合)?

追加。

次の2行を使用して、いくつかのシェルでテストを実行しました。

myTZ="America/Los_Angeles"
unset TZ; { TZ="$myTZ"      date; } >/dev/null; echo -n "  direct $TZ"
unset TZ; { TZ="$myTZ" eval date; } >/dev/null; echo    "  evaled $TZ"

そして、この結果:

/bin/ash        :   direct   evaled America/Los_Angeles
/bin/dash       :   direct   evaled America/Los_Angeles
/bin/sh         :   direct   evaled America/Los_Angeles
/bin/bash       :   direct   evaled
/bin/ksh93      :   direct   evaled America/Los_Angeles
/bin/lksh       :   direct   evaled America/Los_Angeles
/bin/mksh       :   direct   evaled America/Los_Angeles
/bin/zsh        :   direct   evaled
/bin/zsh4       :   direct   evaled 

TZ値は、bashとzshを除くすべてのシェルで実行中のシェルに影響します。

回答:


6

あなたが見つけたように、それはスペックの振る舞いです。しかし、それも理にかなっています。

値をシェルの環境に保持するのは、他の環境変数の値が、コマンドラインに定義のプレフィックスを付けるときに他のコマンドによって保持されるのと同じ理由で、環境で変数を設定しているためです。

特殊な組み込みコマンドは、一般的に任意のシェルの中で最も本質的な多様です- eval基本的にシェルのパーサーのアクセス名であり、setトラックと設定し、オプションとシェルのパラメータをシェル、return/ break/ continueトリガループ制御フロー、trapハンドル信号、exec開く/ファイルを閉じます。これらはすべて基本的なユーティリティであり、通常、シェルの肉とジャガイモをかろうじてラッパーで実装します。

ほとんどのコマンドの実行には、特別なビルトインを呼び出すときに得られないいくつかの階層化された環境- サブシェル環境(必ずしも個別のプロセスである必要はありません)が含まれます。したがって、これらのコマンドのいずれかに環境を設定すると、シェルの環境が設定されます。彼らは基本的にあなたの殻を表すからです。

しかし、そのように環境を保持するコマンドはこれらだけではありません。関数も同じように動作します。そして、エラーが特殊な組み込みコマンドのために異なる動作を-試みcat <doesntexist、次に試すexec <doesntexistだけでもか: <doesntexistとしながら、catコマンドが文句を言うだろう、execまたは:POSIXシェルを殺すでしょう。コマンドラインの拡張エラーについても同様です。彼らは基本的にメインループです。

これらのコマンドは環境を保持する必要はありません。一部のシェルは他のシェルよりも内部をより緊密にラップし、コア機能の一部を公開せず、プログラマーとインターフェースの間にバッファーを追加します。これらの同じシェルは、他のシェルよりも少し遅くなる傾向があります。確かに、仕様に準拠させるには、多くの非標準的な調整が必要です。とにかく、これが悪いことではありません。

fn(){ bad_command || return=$some_value return; }

それは簡単です。余分な環境bad_commandを設定せずに、条件付きで割り当てを行うことなく、他にどのように単純にそのリターンを維持しますか?

arg=$1 shift; x=$y unset y

そのようなものがうまくいきます。インプレーススワップはより簡単です。

IFS=+  set -- "$IFS" x y z
x="$*" IFS=$1 shift
echo "${x#"$IFS"}" "$*"

+x+y+z x y z

...または...

expand(){
    PS4="$*" set -x "" "$PS4" 
    { $1; }  2>&1
    PS4=$2   set +x
}   2>/dev/null

x='echo kill my computer; $y'
y='haha! just kidding!' expand "${x##*[\`\(]*}"

...私が使いたい別のものです...

echo kill my computer; haha! just kidding!

@BinaryZebra-しかし要点は、動作が異なることではありません-他のコマンドに変数を設定すると、それらは他の実行可能ファイルの環境に残ります。シェルの環境で変数を設定すると、それらも持続します。
mikeserv 2015

3

この動作には非常に特定の理由があることがわかります。
何が起きるかの説明は少し長くなります。

割り当てのみ。

(のみ)作成されたコマンドラインは、このシェルの変数を設定します。

$ unset a b c d
$ a=b c=d
$ echo "<$a::$c>"
<b::d>

割り当てられた変数の値は保持されます。

外部コマンド。

外部コマンドの前の割り当ては、そのシェルのみの変数を設定します。

$ unset a b c d
$ a=b c=d bash -c 'echo "one:|$c|"'; echo "two:<$c>"
one:|d|
two:<>

PATHで検索する必要があるコマンドとして「外部」を意味します。

これは通常のビルトイン(例えばcdなど)にも適用されます。

$ unset a b c d; a=b c=d cd . ; echo "<$a::$c>"
<::>

ここまではすべて通常どおりです。

特別な組み込み。

ただし、特別な組み込みの場合POSIXではこのシェルに値を設定する必要があります

  1. 特別な組み込みユーティリティで指定された変数の割り当ては、組み込みが完了した後も有効です。
$ sh -c 'unset a b c d; a=b c=d export f=g ; echo "<$a::$c::$f>"'
<b::d::g>

私はshそれshがPOSIX準拠のシェルであると想定する呼び出しを使用しています。

これは通常使用されるものではありません。

つまり、この特別な組み込みのリストの前にある割り当ては、現在実行中のシェルで割り当てられた値を保持します。

break : continue . eval exec exit export 
readonly return set shift times trap unset

これは、シェルがPOSIX仕様に従って機能する場合に発生します。

結論:

コマンド特別な組み込みコマンドではないことを確認することで、コマンドを1つだけ、つまり任意のコマンドにのみ変数を設定できます。コマンドcommandは通常の組み込みです。これは、関数ではなくコマンドを使用するようにシェルに指示するだけです。この行は、すべてのシェル(ksh93を除く)で機能します。

$ unset a b c d; a=b c=d command eval 'f=g'; echo "<$a::$c::$f>"
<::::g>

このような場合、変数aとbはコマンドcommandの環境用に設定され、その後破棄されます。

代わりに、これは割り当てられた値を保持します(bashとzshを除く)。

$ unset a b c d; a=b c=d eval 'f=g'; echo "<$a::$c::$f>"
<b::d::g>

evalの後の割り当ては、不要な展開から保護するために一重引用符で囲まれていることに注意してください

したがって:コマンド環境に変数を配置するには、次のコマンドを使用しますcommand eval

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.