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
実行されます。A
B
したがって、別のプロセスで実行する必要があります。ほとんどのシェルは子プロセスで実行されます。 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