ファイルの読み取りと書き込み:teeコマンド


10

次のようなコマンドがあることはよく知られています。

cat filename | some_sed_command >filename

コマンドの前に実行される出力リダイレクトとして、ファイルfilenameを消去し、filenameを切り捨てます。

次の方法で問題を解決できます:

cat file | some_sed_command | tee file >/dev/null

しかし、これがどのような場合でも機能するかどうかはわかりません。ファイル(およびsedコマンドの結果)が非常に大きい場合はどうなりますか?まだ読み込まれていないコンテンツをオペレーティングシステムが上書きしないようにするにはどうすればよいですか?どのような場合でも機能するスポンジコマンドもあるようです。ティーより「安全」ですか。


あなたの主な目標は何ですか?(簡単に言えば)
セルギーコロディアズニー

@Sergは物事がどのように機能するかを単純に理解します... kosによって書かれた答えは問題を明確にします
VeryHardCoder

回答:


10

次の方法で問題を解決できます:

cat file | some_sed_command | tee file >/dev/null

いいえ

確率fileは切り捨てられますが、切り捨てられないという保証cat file | some_sed_command | tee file >/dev/nullはありませんfile

予想されるのとは対照的に、パイプ内のコマンドは左から右に処理されません。どのコマンドが最初に選択されるかについての保証はありません。そのため、ランダムに選択されたと考えて、問題のコマンドを選択しないシェルに依存することはできません。

3つのコマンドの間で最初に問題のコマンドが選択される可能性は、2つのコマンドの間で最初に問題のコマンドが選択される可能性よりも低いため、file切り捨てられる可能性は低くなりますが、引き続き発生します。

script.sh

#!/bin/bash
for ((i=0; i<100; i++)); do
    cat >file <<-EOF
    foo
    bar
    EOF
    cat file |
        sed 's/bar/baz/' |
        tee file >/dev/null
    [ -s file ] &&
        echo 'Not truncated' ||
        echo 'Truncated'
done |
    sort |
    uniq -c
rm file
% bash script.sh
 93 Not truncated
  7 Truncated
% bash script.sh
 98 Not truncated
  2 Truncated
% bash script.sh
100 Not truncated

したがって、のようなものは決して使用しないでくださいcat file | some_sed_command | tee file >/dev/nullspongeOliの提案どおりに使用します。

別の方法として、より厳しい環境や比較的小さなファイルの場合、コマンドが実行される前にhere文字列とコマンド置換を使用してファイルを読み取ることができます。

$ cat file
foo
bar
$ for ((i=0; i<100; i++)); do <<<"$(<file)" sed 's/bar/baz/' >file; done
$ cat file
foo
baz

9

sed具体的には、その使用することができ-i、インプレース引数を。開いたファイルに保存するだけです。例:

sed -i 's/ /-/g' filename

より多くのことを行うと仮定して、より強力なことをしたい場合はsed、はい、ファイルに書き込む前にすべての標準入力を「吸収」するspongemoreutilsパッケージから)を使用して全体をバッファリングできます。それは似てteeいますが、機能が少ないです。ただし、基本的な使用法としては、これはほとんどドロップイン置換です。

cat file | some_sed_command | sponge file >/dev/null

安全ですか?間違いなく。おそらく限界があるので、何か巨大なことをしている(そしてsedでインプレース編集できない)場合は、2番目のファイルを編集してから、mvそのファイルを元のファイル名に戻すことができます。これはアトミックである必要があります(したがって、これらのファイルに依存するものは、常にアクセスする必要がある場合でも壊れません)。



0

ああ、それだけでspongeはありません。moreutilsこれを正しく動作させるために取得する必要はありません。次の2つの要件を満たしている限り、どのメカニズムでも機能します。

  1. 出力ファイルの名前をパラメーターとして受け入れます。
  2. すべての入力が処理されると、出力ファイルが作成されます。

ご覧のとおり、OPが参照しているよく知られている問題は、パイプラインでコマンドの実行を開始する前でも、シェルがパイプの動作に必要なすべてのファイルを作成するため、実際に切り捨てられるのはシェルです。コマンドが実行を開始する前に出力ファイル(残念ながら入力ファイルでもあります)。

teeそれは出力ファイルにパイプストレートの作成などの悪いものと本質的であるので、それは常に、開始直後に出力ファイルを作成します。それは第二の要件を満たしていないため、コマンドは、最初の要件もそれを満たしても、仕事をしません。(実際には、出力ファイルが切り捨てられる前に非決定的なランダム遅延が導入されるため、さらに悪いことです。そのため、実際には機能しないものの、機能すると考えるかもしれません。)

したがって、この問題を解決するために必要なのは、出力を生成する前にすべての入力をバッファリングするコマンドであり、出力ファイル名をパラメータとして受け入れることができるため、出力をパイプに送る必要がありません。出力ファイル。そのようなコマンドの1つはshufです。したがって、以下は同じことを実行spongeします。

    shuf --output=file --random-source=/dev/zero 

--random-source=/dev/zero一部のトリックshufまったくシャッフルを行うことなく、そのことをやってには、それはそれを変更することなく、あなたの入力をバッファリングします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.