変数突然変異を見えないようにするために関数呼び出し出力をリダイレクトする効果を理解する

変数突然変異を見えないようにするために関数呼び出し出力をリダイレクトする効果を理解する

Macのzsh 5.9で奇妙な動作が観察されました。 (次はエラーを再現するために単純化したもので、理解できません。) 端末で実行すると

$> function assigner() { OPTIONS=mutated } 
$> function main() { typeset OPTIONS; assigner; echo $OPTIONS }  
$> main

この出力は次のようになります。

mutated

しかし、内部関数呼び出しの出力をリダイレクトするために基本関数を微妙に変更すると...

$> function main() { typeset OPTIONS; assigner | cat; echo $OPTIONS }

これで結果が空になります

私のOPTIONS varはもはや満たされていません。ここで何が起こっているのでしょうか?

答え1

パイプはプロセス間通信メカニズムです。

A | BパイプラインからA標準入力にパイプされた標準出力と並列にB実行されます。AB

したがって、別のプロセスで実行する必要があります。ほとんどのシェルは子プロセスで実行されます。 kshの元の実装と同様に、zshはA子プロセスでのみ実行されます(もちろん、B外部コマンドであってもまだ子プロセスで実行されます)。

したがって、サブシェルプロセスで実行されるassigner | catためassigner、親シェルプロセスの変数ではなく、そのサブシェルの変数のみを変更します。パイプラインが終了すると、変数の変更は失われます。

assignerここで親プロセスで実行したいが、その出力をcat別のプロセスにパイプする場合は、代わりに出力をassigner実行中のプロセスにリダイレクトできますcat

assigner > >(cat)

catまたは、セカンダリプロセスとして実行します。

コプロセスはstdinとstdoutをパイプにリダイレクトするため、基本プロセスはデータを供給して日付を取得できますが、追加のファイル記述子を使用して元のstdoutを保存および復元するとstdout部分を削除できます。

assigner() OPTIONS=mutated

{ coproc cat >&3 3>&-; } 3>&1 # start cat as coproc with stdout restored
cat_pid=$!                    # record its pid
assigner >&p                  # run assigner with stdout redirected to coproc
coproc :                      # dummy coproc to close the pipes to the
                              # previous one.
wait $cat_pid                 # make sure cat has returned before running
                              # the next commands.
echo $OPTIONS                 

(望むよりさまざまなシェルでcoprocコマンドを使用する方法は?コプロセスの使用方法の詳細)

あるいは、assigner別の fd を使用し、実行中の同じサブシェルに設定された変数を使用して、生の stdout を再び使用可能にすることもできます。

assigner() OPTIONS=mutated

{
  {
    assigner
    echo $OPTIONS >&3
  } | cat
} 3>&1

関連情報