考慮する:
numbers="1 111 5 23 56 211 63"
max=0
for num in ${numbers[@]}; do
[ $num -gt $max ]\
&& echo "old -> new max = $max -> $num"\
&& max=$num
done | tee logfile
echo "Max= $max"
max変数を削除すると| tee logfile
211として正しく印刷されますが、そのままにしてくださいMax= 0
。
どうしたの?
ベストアンサー1
パイプの各側面はサブシェルで実行されます。サブシェルは、同じ状態で開始し、独立して進化する元のシェルプロセスのコピーです。子シェルに設定された変数は、親シェルにエスケープできません。
Bashでは、パイプを使用する代わりに次のものを使用できます。プロセスの交換。ループが元のシェルとサブシェルtee
でのみ実行されることを除いて、これはパイプとほぼ同じように動作します。
for num in "${numbers[@]}"; do
if ((num > max)); then
echo "old -> new max = $max -> $num"
max=$num
fi
done > >(tee logfile)
echo "Max= $max"
その間、スクリプトでいくつか変更しました。
- 変数置換を省略する理由がわからない場合は、必ず二重引用符を使用してください。
&&
あなたがそれを意味するときにそれを使用しないでくださいif
。コマンドの戻り状態を使用すると、読みにくくなり、同じ動作をしません。- 私はbash固有の設定を使用しているので、次のものも使用できます。算術用語。
パイプソリューションとサブシェルソリューションの間にはいくつかの違いがあります。パイプ処理されたコマンドは両方のコマンドが終了すると完了しますが、プロセス置換を含むコマンドはプロセス置換のプロセスを待たずに基本コマンドが終了すると完了します。これは、スクリプトの完了時にログファイルが完全に記録されていない可能性があることを意味します。
別のアプローチは、別のチャネルを使用してサブシェルから元のシェルプロセスに通信することです。あなたはできます一時ファイルの使用(柔軟ですが実行するのは難しいです。一時ファイルを適切なディレクトリに書き込み、名前の競合を防ぎ(使用mktemp
)、エラーが発生しても削除します。別のパイプを使用してください結果を渡し、コマンド置換によって結果を取得します。
max=$({ { for … done;
echo "$max" >&4
} | tee logfile >&3; } 4>&1) 3&>1
出力はtee
ファイル記述子3に移動し、これはスクリプトの標準出力にリダイレクトされます。の出力はecho "$max"
ファイルディスクリプタ4に移動し、これはコマンドの代わりにリダイレクトされます。