テキストファイルを変更すると、変数 "line"を読み取るbash whileループは自動的に更新されますか?

テキストファイルを変更すると、変数

たとえば、「He​​llo」を含む hello.txt というテキストファイルがあります。読み取り可能なwhileループがある場合:

while IFS= read -r line
do
    echo $line > hello.txt # whatever that inserts/ edits something in the text file
done < hello.txt

whileループの読み取りが変更されるか、無限ループが発生しますか?それとも、whileループはテキストファイルの変更を読み取ることができませんか?更新/修正されたテキストファイルを強制的に読み取ることができますか?

ベストアンサー1

ループの入力とread出力は同じファイルに接続されます。はい。echo

この場合、出力リダイレクトを設定するたびに、個々の呼び出しごとにechoファイルが切り捨てられます。入力の読み出し位置は、最後の読み出しラインの直後の元の位置にとどまります。ここではまったく同じ行を印刷するので、読み取り位置は新しいEOFにあり、ループは1回の反復後に終了します。ファイルの最初の最初の行をファイルの唯一の内容として保持します。


ただし、長いテキストを出力すると、ループは以前に出力された内容の一部を読み取ることができます。

たとえば、考えてみましょう。

$ cat hello.txt
abc
def
ghi
$ cat test.sh
while IFS= read -r line
do
    echo "$line"  # to terminal, to see what is read
    echo "something something $line" > "$1"
done < "$1"

これでスクリプトを実行すると、hello.txt次の行が残ります。

something something g something abc

最初の行の後にはファイルが含まれており、something something abc読み取り位置は最初の行にありますtabcおよび改行文字を読み取るため)。読み取りthing something abcと印刷を繰り返しますsomething something thing something abc(ファイルの切り取り)。読み取り位置はまだ中間にあるため、最終的にEOFに達するまでループが繰り返されます。

read特に、読み取り位置は正確に行の末尾に保持されます。これはたとえば(標準準拠)と同じですhead -n1が、他の多くのユーティリティはブロック全体を読み込み、読み込み位置を短い入力のために元のファイルの末尾に残します。ここで結果が変わります。


一方、出力リダイレクトをに置き換えると、>> fileすべての書き込みがファイルに追加され、読み取り位置は決して終わりに達しません(ループは繰り返しごとに1行だけ読み書きするため)。無限ループが発生します。


繰り返しますが、削除するループ内の出力ファイルは、リダイレクト前にすべてが変更されます。

while IFS= read -r line
do
    rm "$1"
    echo "$line"  # to terminal, to see what is read
    echo "something something $line" > "$1"
done < "$1"

これで名前がなくなったため、出力リダイレクトから同じファイルにアクセスできなくなります。新しいファイルが作成されます。読み取りファイルハンドルはまだファイルに接続されています。もはや開いていない場合にのみ最終的に削除されます。元のバージョンでこれを実行すると、hello.txt新しいバージョンが残りますhello.txt

something something ghi

出力ファイルが入力から独立しているので、ループは入力を最後まで読み込みます。繰り返しごとに削除され、再生成されます。


ループの繰り返しごとにファイルを削除することはあまり役に立ちませんが、一度だけ削除された場合は、リダイレクトを少し移動する必要があります。これにより、ファイル全体が繰り返され、すべての行に次の名前が付いた新しいファイルが残りますsomething something

exec < "$1"  # input redirection for the whole shell script
rm "$1"
exec > "$1"  # symmetrically, output, creating a new file
             # with the same name
while IFS= read -r line
do
    echo "$line"  >&2 # to terminal via stderr, to see what is read
    echo "something something $line"
done

おすすめ記事