特定のパターンが現れる行からファイルを切り取る方法は?

特定のパターンが現れる行からファイルを切り取る方法は?

サイズを小さくする必要があるファイルが多すぎます。私はほとんどの(すべてではない)ファイルに情報を失うことなく切り取ることができる終わりセクションがあることを発見しました。

Data 1
Data 2
something_unimportant_here END DATA
Rubbish 1
Rubbish 2

「END DATA」を含む行とその後のすべての行を削除し、そのパターンを含むファイルのみを変更してファイル(したがってすべて終了)を編集するにはどうすればよいですか?これにより、ディスクへの書き込みアクセスが最小限に抑えられます(多くのファイルと遅いディスク)。

可能であれば、ファイルの構文が正しいままになるように、ファイルに新しい最後の行(マイクローズタグ)を追加したいと思います。繰り返しますが、パターンを含むファイルでのみ可能です。

私はed次のようなものを使うつもりです。

echo ',s/END DATA/ ???? '\\n'q'\\n'wq' | ed "$file"

ところで管理できないようですね? ? ?部分的に本当です。

予想出力:

Data 1
Data 2
NEW END

答え1

sed -i// perl -i/ ¹などのファイルの新しいコピーを作成するのではなく、ファイルを直接カットしてこれを行うことがedできるはずです。gawk -i /usr/share/awk/inplace.awk使用perl:

find . -name '*.txt' -type f -exec perl -ne '
  BEGIN{@ARGV=map{"+<$_"}@ARGV} # open files in read+write mode in the
                                # while(<>) loop implied by -n
  if (/END DATA/) {
    seek ARGV,-length,1; # back to beginning of matching line
    print ARGV "NEW END\n";
    truncate ARGV, tell ARGV;
    close ARGV; # skip to next file
  }' {} +

perl一致するものが見つかると、読み取りは停止され、それが NEW END\n記録される唯一のものであるため、I / Oが最小化されます。また、内部書き込みを行うため、ファイルメタデータ(所有権、権限、acl、スパース...)が保存され、ハードリンクが破損することはありません。

-exec {} +通話回数も最小限に抑えることができますperl


^使用しないでください-i inplace現在の作業ディレクトリから最初に拡張機能をgawkロードしようとすると、誰かがそのディレクトリにマルウェアを植えた可能性があります。システムに付属の拡張プログラムのパスは異なる場合があります。出力を参照してください。inplaceinplaceinplace.awkinplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

答え2

探しているコマンドシーケンスは次のとおりです。

/END DATA/,$d
q
.a
NEW END
.
wq

または一行で

printf '%s\n' '/END DATA/,$d' 'q' '.a' 'NEW END' '.' 'wq'

wq(テストで置き換え可能です,p。)

前任者。与えられた

$ cat file
Data 1
Data 2
something_unimportant_here END DATA
Rubbish 1
Rubbish 2

それから

$ printf '%s\n' '/END DATA/,$d' 'q' '.a' 'NEW END' '.' 'wq' | ed -s file

与えられた

$ cat file
Data 1
Data 2
NEW END

答え3

そしてGNU grepGNU sed

grep -lZ 'END DATA' *.txt | xargs -0 sed -i -e '/END DATA/,${//i foo' -e 'd}'

これは、すべてのファイルが拡張子で終わる現在のディレクトリに*.txtあると仮定します。.txtファイルを繰り返し検索する必要がある場合は、オプションもサポートされGNU grepます-r/-R

/END DATA/,$動作ライン範囲

//i fooこれは//以前に使用された正規表現と一致します。/END DATA/つまり、iコマンドは必要に応じて新しいクローズタグを追加します。

iコマンドは改行で区切る必要があるため、オプションは-e範囲dに一致するすべての行を削除するためにコマンドを区切るために使用されます。

代わりにこの方法を使用することもできますが、一度に1つのファイルのみが渡されますsed

grep -lZ 'END DATA' *.txt | xargs -0 -n1 sed -i -e '/END DATA/{i foo' -e 'Q}'

答え4

このpython3.8ソリューションは、Stephaneの内部ソリューションに大まかに基づいています。truncate 解決策いくつかの違いがあります。 1.コードはディレクトリナビゲーションのために外部ユーティリティに依存しません。 2.ファイルは、END DATA文字列の検索を容易にするためにメモリマップされます。

コードを.pyファイルに入れ、ディレクトリ名をパラメータとして渡します。

import mmap
import os
import sys
from contextlib import closing

def yield_all_files(dir_):
    for root, dir_, files in os.walk(dir_):
        yield from (os.path.join(root, file_) for file_ in files if file_.endswith('.txt'))

if __name__ == '__main__':
    old = b'END DATA'
    new = b'NEW END\n'
    dir_ = sys.argv[1]
    for file_ in yield_all_files(dir_):
        with open(file_, mode='r+b') as f:
            with closing(mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_WRITE)) as mm:
                if (loc := mm.find(old)) > -1:
                    mm.seek(loc)
                    mm.write(new)
                    mm.resize(mm.tell()) 

関連情報