ファイルの出力をそれ自体にリダイレクトすると、なぜ空のファイルが生成されるのですか?


19

ファイルの出力をそれ自体にリダイレクトすると、なぜ空のファイルが生成されるのですか?

Bashに記載されている理由

less foo.txt > foo.txt

そして

fold foo.txt > foo.txt

空を生成しますfoo.txtか?などの追加less eggs.py >> eggs.pyは、テキストのコピーを2 eggs.pyつ作成するため、上書きするとテキストのコピーが1つ作成されることが予想されます。

これはバグだと言っているのではないことに注意してください。これは、Unixの奥深い何かへのポインタである可能性が高いです。


回答:


20

を使用する>と、ファイルは切り捨てモードで開かれるため、コマンドが読み取ろうとする前にその内容は削除されます。

を使用する>>と、ファイルは追加モードで開かれるため、既存のデータが保持されます。ただし、この場合、同じファイルを入力および出力として使用することは依然としてかなり危険です。ファイルが読み取り入力バッファサイズに収まらないほど大きい場合、ファイルシステムがいっぱいになる(またはディスククォータに達する)まで、ファイルのサイズが無限に大きくなる可能性があります。

インプレース変更をサポートしないコマンドでファイルを入力および出力の両方として使用する場合は、いくつかの回避策を使用できます。

  • ユーティリティの実行中にエラーが発生しなかった場合にのみ、中間ファイルを使用して元のファイルを上書きします(これが最も安全で一般的な方法です)。

    fold foo.txt > fold.txt.$$ && mv fold.txt.$$ foo.txt
  • エラーや中断が発生した場合、潜在的な部分的または完全なデータ損失を犠牲にして、中間ファイルを避けてください。この例では、ファイルが削除される前に、の内容がfoo.txt入力としてサブシェル(括弧内)渡されます。サブシェルがデータの読み取り中にそれを開いたままにしているため、前のiノードは存続します。内部ユーティリティ(ここではfold)が同じ名前(ただし)で記述したファイルfoo.txt)古いディレクトリエントリが技術的に削除されているため、異なるiノードを指します。プロセス中に同じ名前の2つの異なる「ファイル」があります。サブシェルが終了すると、古いiノードが解放され、そのデータが失われます。古いファイルと新しいファイルの両方を一時的に保存するのに十分なスペースがあることを確認してください。そうしないと、データが失われます。

    (rm foo.txt; fold > foo.txt) < foo.txt

3
spongemoreutilsからも役立ちます。fold foo.txt | sponge foo.txt–または、実行するfold foo.txt | sponge !$必要があります。
slhck

@slhck確かに、スポンジも仕事をすることができます。ただし、POSIXで指定されておらず、OSのようなUnixの主流でもないため、存在する可能性は低いです。
jlliagre

それ存在することができないようではありません;)
slhck


0

bashでは、ストリームリダイレクト演算子... > foo.txtfoo.txt 、左オペランドを評価する前になります。

回避策としてコマンド置換を使用し、その結果を出力する場合があります。このソリューションは、他の回答よりも追加の文字が少なくなります。

printf "%s\n" "$(less foo.txt)" > foo.txt

注意:このコマンドは、の末尾の改行を保持しませんfoo.txt。詳細については、以下のコメントセクションをご覧ください

ここでは、サブシェル$(...)はストリームリダイレクト演算子のに評価される>ため、情報が保存されます。


@KamilMaciorowski:実際、ありますtmp=$(cmd; printf q);  printf '%s' "${tmp%q}"。しかし、あなたはこの答えに別の問題を見逃しました。「コマンド置換」を意味するときに「サブシェル」と言います。はい、コマンド置換は一般にサブシェルですが、その逆はできません。一般に、サブシェルはこの問題の助けにはなりません。
スコット

@KamilMaciorowskiこのすべてを見逃したのはとても気分が悪い。これをすべて指摘してくれてありがとう。(4)番目のポイント:バッククォートはトリックを行います。つまり、末尾の改行を保持しますか?
ルイヤコブレーベル

@Scott、返信ありがとう。「コマンド置換」の「サブシェル」を変更しました。ちなみに、この2つの違いは何でしょうか。
ルイヤコブレーベル

いいえ、バッククォート(バックティック)は、後続の改行文字も削除します。
カミル・マチオロフスキー

それでは、今のところ警告メッセージを追加しました。解決策が見つかったら削除します。
ルイヤコブレーベル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.