ターゲットファイルがまだ存在しない場合、 `>>`によるリダイレクトは `>`と同等ですか?


80

Bashやshのようなシェルを考えてください。ターゲットファイルが存在する場合の基本的な違い>>>それ自体が明らかになります。

  • > ファイルをゼロサイズに切り捨ててから書き込みます。
  • >> 切り捨てられず、ファイルの最後に書き込み(追加)されます。

ファイルが存在しない場合は、サイズがゼロで作成されます。その後に書かれた。これは両方の演算子に当てはまります。ターゲットファイルがまだ存在しない場合、演算子は同等に見えるかもしれません。

彼らは本当にですか?

回答:


107

tl; dr

いいえ。>>本質的に「常にファイルの終わりを探します」>が、最後に書き込まれた場所へのポインタを維持します。


完全な答え

(注:すべてのテストはDebian GNU / Linux 9で行われました)。

もう一つの違い

いいえ、同等ではありません。別の違いがあります。ターゲットファイルが以前に存在したかどうかに関係なく、それ自体が現れる場合があります。

それを観察するには、データを生成するプロセスを実行し、>または>>(たとえばpv -L 10k /dev/urandom > blob)でファイルにリダイレクトします。実行させて、ファイルのサイズを変更します(例:)truncate。常に最後に追加し>ながら、その(成長している)オフセットを維持することがわかり>>ます。

  • ファイルを小さいサイズに切り捨てる場合(サイズをゼロにすることができます)
    • >気にせず、何も起こらなかったかのように、希望するオフセットで書き込みます。オフセットの切り捨てがファイルの終わりを超えた直後に、これによりファイルは元のサイズに戻り、さらに大きくなります。欠損データはゼロで埋められます(可能であれば、まばらな方法で)。
    • >> 新しい終わりに追加され、ファイルは切り捨てられたサイズから大きくなります。
  • ファイルを拡大する場合
    • >気にせず、何も起こらなかったかのように、希望するオフセットで書き込みます。サイズを変更した直後、オフセットはファイル内のどこかにあります。これにより、オフセットが新しい終わりに達するまで、ファイルはしばらくの間成長を停止し、その後、ファイルは正常に成長します。
    • >> 新しい終わりに追加され、ファイルは拡大されたサイズから大きくなります。

もう1つの例は>>、データ生成プロセスが実行されてファイルに書き込まれているときに、余分なものを(個別に)追加することです。これは、ファイルの拡大に似ています。

  • 生成プロセス>は、希望するオフセットで書き込み、最終的に余分なデータを上書きします。
  • 生成プロセス>>は、新しいデータをスキップし、それを超えて追加します(競合状態が発生する可能性があり、2つのストリームがインターリーブされる可能性がありますが、データは上書きされません)。

実際には問題になりますか?この質問があります:

stdoutで多くの出力を生成するプロセスを実行しています。すべてをファイルに送信[...]何らかのログローテーションプログラムを使用できますか?

この答えは、解決策は次のように機能logrotateするcopytruncateオプションを使用していると言います

古いログファイルを移動し、オプションで新しいログファイルを作成する代わりに、コピーの作成後に元のログファイルを切り捨てます。

上記で書いたように、でリダイレクトする>と、切り捨てられたログがすぐに大きくなります。スパース性は1日を節約し、大きなディスク容量を浪費する必要はありません。それにもかかわらず、連続する各ログには、完全に不要な先行ゼロが増えます。

ただし、logrotateまばらさを保持せずにコピーを作成する場合、これらの先行ゼロには、コピーが作成されるたびに、より多くのディスクスペースが必要になります。ツールの動作については調査していませんが、その場での圧縮や圧縮(圧縮が有効になっている場合)で十分に賢いかもしれません。それでも、ゼロは問題を引き起こすか、せいぜい中立でしかありません。それらで良いものはありません。

この場合、ターゲットファイルがまだ作成されようとしている場合でも、>>代わりにを使用する>方がはるかに優れています。


性能

ご覧のとおり、2つの演算子は、開始時だけでなく、後でも異なる動作をします。これにより、多少の(微妙な?)パフォーマンスの違いが生じる可能性があります。今のところ、それを支持または反証する意味のあるテスト結果はありませんが、パフォーマンスが一般的に同じであると自動的に仮定すべきではないと思います。


9
そう>>しながら、「常にファイルの末尾に求める」本質的に>最後に書き込まれた場所へのポインタを維持しています。動作方法にも若干のパフォーマンスの違いがあるかもしれないようです...
木梅

10
システムコールレベルで>>O_APPENDフラグをopen()使用ます。実際に>O_TRUNC、を使用しますが、使用し>>ません。の組み合わせO_TRUNC | O_APPENDも可能です。シェル言語はその機能を提供していません。
-ilkkachu

3
@jjmontes、標準ソースは、POSIXのようになります。pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/...もちろんバッシュのマニュアルには、それがサポートする非標準のものを含むリダイレクト演算子の説明を、持っている:gnu.org/ software / bash / manual / html_node / Redirections.html
ilkkachu

2
@ilkkachuあなたのコメントの後に私が疑問に思っていたO_APPENDの詳細を説明しているので、これが興味深いことに気付きました:):stackoverflow.com/questions/1154446/…–
jjmontes

1
@Mokubai、健全なOSは開いているときにファイルの長さが手元にあり、フラグをチェックしてオフセットを最後に移動すると、他のすべてのブックキーピングで消えます。ただし、それぞれO_APPENDlseek()前にエミュレートしようとするとwrite()異なりますが、余分なシステムコールのオーバーヘッドが発生します。(そしてもちろん、別のプロセスがwrite()間にある可能性があるため、機能しません。)
ilkkachu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.