複数のオープン/クローズハンドルを回避するためにbashでファイルハンドルを開く構文または方法は何ですか?

複数のオープン/クローズハンドルを回避するためにbashでファイルハンドルを開く構文または方法は何ですか?

ファイルを開いたり生成したり、その中にループでたくさんの行を書きたいです。

基本的に私はそうします:

for i in {1..100}; do
  echo "line $i">>test.log
done

私の理解は、OSがtest.logファイルi時間を開いたり閉じたりすることです。

私は別の構文/アプローチを見つけました。

exec {log}>>test.log
for i in {1..1000}; do
  echo "line $i" >& $log
done
{log}>&-

すでに開いているファイルの使用 exec {log}>>test.log中にファイルを開いたり作成したり、開いたままにしたいとしますか?ファイルハンドルが閉じますか?echo "line $i" >& $log{log}>&-

この方法は何ですか?ユースケースの詳細情報はどこにありますか?

答え1

表示されたユースケースでは、for複合コマンドの標準出力をリダイレクトできます。これにより、ファイル記述子を処理する必要がなくなります。

for i in {1..100}; do
    printf 'line %d\n' "$i"
done >test.log

これにより、名前付きファイルが作成または切り捨てられ、test.logループ内の標準出力ストリームのすべての出力(他の場所で明示的にリダイレクトされない限り)はそのファイルに移動されます。>>ファイルを切り捨てるのではなく、ファイルに追加するために使用されます。ファイルは一度だけ開きます。

他の複合コマンドでも同じことができます。

if [ -e file ]; then
    echo 'file exists'
else
    echo 'file does not exist'
fi >out.log
{
   echo 'line 1'
   cat some-file
   echo 'last line'
} >lines.txt

答え2

まず、fdは単一ループ内でのみ使用されるため、後で手動で閉じる必要なくループにリダイレクトを適用することも可能です。

for i in {1..100}; do
    echo "line $i"
done >>test.log

ここでループの先頭からファイルを消去するには、追加のリダイレクトを使用するのではなく、切り捨てリダイレクトを使用することも> test.logできます。>>ループ全体に対して同じファイルハンドルなので、ループ反復間で切り捨てられません。

execリダイレクトの処理に使用する特定の名前があるかどうかはわかりません。これは、シェル全体に適用されるリダイレクトを変更する方法です。もう少し奇妙なことは、別の使用法がexecシェル全体を置き換えるコマンドで使用されることです。ただし、ここではシェルは引き続き実行されます。

1つは{var}>filename非標準拡張です。Bashのマニュアルに記載されている:

各リダイレクトの前にファイル記述子を含めることができますが、次の形式の単語が先頭になることもあります。変数名}。この場合、>&-と<&-を除くすべてのリダイレクト演算子に対して、シェルは10より大きいファイル記述子を割り当て、{変数名}。 >&-または<&-の前に{がある場合変数名}、varname値は閉じるファイル記述子を定義します。もし{変数名指定した場合、リダイレクトはコマンドの範囲外で持続するため、シェルプログラマーはファイル記述子の寿命を手動で管理できます。

また、少なくともkshとzshでサポートされています(Bashのためにそれらの1つから借りることができます)。

自動割り当ては必要ありませんが、固定金額を使用できます。たとえば、次のようになります。

exec 9>>test.log
for i in {1..100}; do
    echo "line $i" >&9
done
exec 9>&-

fd番号を直接追跡するだけです。 3から9までの数字は、スクリプト作成者が自由に使用できる必要があります。 0、1、2 は標準ファイル記述子で、10 以上はシェルで内部で使用できます。

関連情報