크기를 줄여야 하는 파일이 너무 많습니다. 나는 대부분의 (전부는 아님) 파일에 정보 손실 없이 잘라낼 수 있는 끝 섹션이 있다는 것을 발견했습니다.
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
현재 작업 디렉터리(as or)에서 확장 기능을 먼저 gawk
로드 하려고 하면 누군가가 해당 디렉터리에 악성 코드를 심었을 수 있습니다. 시스템과 함께 제공되는 확장 프로그램 의 경로 는 다를 수 있습니다. 출력을 참조하세요.inplace
inplace
inplace.awk
inplace
gawk
gawk '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 grep
그리고GNU 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
범위와 일치하는 모든 줄을 제거하기 위해 명령을 구분하는 데 사용됩니다.
대안으로 이 방법을 사용할 수도 있지만 한 번에 하나의 파일만 전달됩니다 sed
.
grep -lZ 'END DATA' *.txt | xargs -0 -n1 sed -i -e '/END DATA/{i foo' -e 'Q}'
답변4
이 python
3.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())