質問しました

質問しました

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

$ 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

多くのテストの後、これは特定のシェルでのみ発生することがわかりました。 dash、kshでは発生しますが、bashまたはzshでは発生しません。

質問しました

問題は次のとおりです。

  • 現在のシェルに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を除くすべてのシェルで実行されているシェルに影響します。

答え1

あなたが見つけたように、これは標準的な動作です。しかし、それも意味があります。

コマンドラインに定義を追加すると、他のコマンドが他の環境変数の値を保持するのと同じ理由で、値はシェル環境に保持されます。環境で変数を設定することです。

これ特殊組み込み機能通常、すべてのシェルの最も重要な種類 -eval基本的に、シェルパーサーのアクセス可能な名前、setシェルオプション、およびシェル引数の追跡と設定、return//ループ制御フロートリガ、break信号処理、ファイルの開閉。これは、包装紙をほとんどまたはまったく使用せずに入手できる基本的なユーティリティです。continuetrapexec

ほとんどのコマンドを実行するには、一部の階層化環境が必要です。サブシェル環境(必ずしも別のプロセスである必要はありません)- 特別な組み込み関数を呼び出すと不明です。したがって、これらのコマンドのいずれかに環境を設定すると、シェルの環境も設定されます。基本的にはシェルを表すからです。

しかし、このように環境を保存するコマンドはこれだけではありません。関数も同じことを行います。エラーは特殊な組み込みコマンドに対して異なる動作をします。cat <doesntexisttryの後に試したり、コマンドが文句を言ったり、POSIXシェルを終了したりするexec <doesntexistときも同様です。コマンドラインの拡張エラーも同様です。彼ら: <doesntexistcatexec:メインループ、基本的に。

これらのコマンドは持つ環境を維持するために、一部のシェルは内部を他のシェルよりもしっかりと包み込み、コア機能をあまり露出させず、プログラマとインタフェースの間にバッファを追加します。これらの同じシェルは他のシェルよりも遅くなる可能性があります。もちろん、規制を遵守するには、非標準的な調整がたくさん必要です。とにかくこれではありません。悪いもの:

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!

答え2

これらの行動には非常に具体的な理由があることがわかりました。
何が起こったのかについての説明が少し長かった。

ちょうど宿題。

割り当てで構成されたコマンドライン(のみ)は変数を設定します。これシェル。

$ 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>

POSIX互換シェルであると仮定してsh呼び出しを使用しています。sh

これは一般的に使用されるものではありません。

つまり、この特殊な組み込み関数のリストの前にある割り当ては、現在実行中のシェルで指定された値を維持する必要があることを意味します。

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はコマンド環境に設定されてから削除されます。

代わりに、割り当てられた値が保持されます(bashおよびzshを除く)。

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

ノートeval以降の割り当ては、不要な拡張を防ぐために一重引用符で囲まれています。

したがって、コマンド環境に変数を入れるには、次のようにしますcommand eval

関連情報