큰 .tgz에서 파일을 효율적으로 제거


14

gzip으로 압축 된 tar-ball compressArchive.tgz (+100 개 파일, 총 + 5GB)가 있다고 가정합니다.

주어진 파일 이름 패턴 (예 : prefix * .jpg)과 일치하는 모든 항목을 제거하고 유골을 gzip : ed tar-ball에 다시 저장하는 가장 빠른 방법은 무엇입니까?

기존 아카이브를 교체하거나 새 아카이브를 생성하는 것은 중요하지 않습니다.


답변:


14

GNU tar를 사용하면 다음을 수행 할 수 있습니다.

pigz -d < file.tgz |
  tar --delete --wildcards -f - '*/prefix*.jpg' |
  pigz > newfile.tgz

bsdtar:

pigz -d < file.tgz |
  bsdtar -cf - --exclude='*/prefix*.jpg' @- |
  pigz > newfile.tgz

( pigz의 멀티 스레드 버전 gzip)입니다.

다음과 같이 파일 자체를 덮어 쓸 수 있습니다.

{ pigz -d < file.tgz |
    tar --delete --wildcards -f - '*/prefix*.jpg' |
    pigz &&
    perl -e 'truncate STDOUT, tell STDOUT'
} 1<> file.tgz

그러나 결과가 원본 파일보다 압축률이 낮은 경우 (두 번째 pigz파일은 아직 읽지 않은 파일의 영역을 덮어 쓸 수 있음 ) 특히 위험합니다 .


대답 해 주셔서 감사합니다. 다음 주에 벤치 마크를 실행하여 내 아카이브와 시스템에 어떤 기능이 더 적합한 지 확인하고이를 수락합니다.
Aksel Willgert 2016 년

8

쉬운 방법으로 할인하지 마십시오 : 목적에 따라 충분히 빠를 수 있습니다. 로 에서 avfs 디렉토리로 아카이브에 액세스합니다 :

cd ~/.avfs/path/to/original.tar.gz\#
pax -w -s '/^.*\.jpg$//' | gzip >/path/to/filtered.tar.gz        # POSIX
tar -czf /path/to/filtered.tar.gz -s '/^.*\.jpg$//' .            # BSD
tar -czf /path/to/filtered.tar.gz --transform '/^.*\.jpg$//' .   # GNU

더 원시적 인 도구를 사용하여 먼저 파일을 제외한 파일을 추출한 .jpg다음 새 아카이브를 작성하십시오.

mkdir tmpdir && cd tmpdir
<original.tar.gz gzip -d | pax -r -pe -s '/^.*\.jpg$//'
pax -w . | gzip >filtered.tar.gz
cd .. && rm -rf tmpdir

당신의 타르에있는 경우 --exclude:

mkdir tmpdir && cd tmpdir
tar -xzf original.tar.gz --exclude='*.jpg'
tar -czf filtered.tar.gz .
cd .. && rm -rf tmpdir

그러나 루트로 실행하지 않으면 파일 소유권과 모드가 뒤섞 일 수 있습니다. 최상의 결과를 얻으려면 빠른 파일 시스템의 임시 디렉토리 인 tmpfs를 사용하십시오.

아카이버가 통과 (즉, 아카이브 읽기 및 아카이브 쓰기) 역할을하는 지원이 제한되는 경향이 있습니다. GNU 타르는 아카이브에서 멤버를 삭제할 수 있습니다--delete동작 옵션 (이하 " --delete시 옵션이 제대로 작동하려면보고되었다 tar에서 필터 역할 stdinstdout."), 그리고 아마 당신의 최선의 선택입니다.

몇 줄의 Python에서 강력한 보관 필터를 만들 수 있습니다. 그 tarfile라이브러리는 읽기 및 비 시크 스트림에서 쓰기, 당신은 수정, 필터, 이름 바꾸기에 파이썬에서 임의 코드를 사용할 수 있습니다 ...

#!/usr/bin/python
import re, sys, tarfile
source = tarfile.open(fileobj=sys.stdin, mode='r|*')
dest = tarfile.open(fileobj=sys.stdout, mode='w|gz')
for member in source:
    if not (member.isreg() and re.match(r'.*\.jpg\Z', member.name)):
        sys.stderr.write(member.name + '\n')
        dest.addfile(member, source.extractfile(member))
dest.close()

또한 tar 파일이 처음 작성된 것과 동일한 uid <=> 사용자 이름 매핑이있는 시스템에서 수행되지 않으면 루트로 실행되는 경우 uid / 사용자 이름을 맹 글링합니다. ACL, 확장 속성도 영향을받을 수 있습니다. 을 사용 tar하면 p옵션 을 추가 할 수 있습니다.
Stéphane Chazelas 16:19에

2

Mac OSX에 포함 된 tar를 사용하면 다음과 같이 할 수 있습니다.

tar -czf b.tgz --exclude '*.jpg' @a.tgz
mv b.tgz a.tgz

1

이렇게하려면 로컬 디렉토리에서 .tgz 파일의 모든 내용을 추출한 다음 원하지 않는 파일을 지우고 .tgz를 다시 압축해야합니다.

길고 충분한 여유 디스크 공간이 필요하지만 내가 아는 한 다른 방법은 없습니다.

/tmpdir/withalotofspace여유 공간이 충분한 경로가 이미 있다면 (을 사용하여 확인 df -h /tmpdir/withalotofspace) 다음과 같이 할 수 있습니다.

$ cd /tmpdir/withalotofspace
$ tar -xvfz /path/to/compressedArchive.tgz
$ find /tmpdir/withalotofspace/ -type f -iname '*.jpg' -delete
$ tar -cvzf /path/to/purgedcompressedArchive.tgz .

다른 답변에서 알 수 있듯이 파이핑을 통해 압축되지 않은 데이터를 디스크에 저장할 필요가 없습니다.
Tobias Kienzler

0

@Gilles의 답변이 마음에 들지만 더 단순화 할 수 있습니다. 압축을 풀면 예를 들어 gunzip foo.tgz파일이 foo.tar이고 파일을 제거 할 수 있습니다 tar -f foo.tar --delete file|directory. 다음은 tar 파일에서 디렉토리를 제거하는 예입니다.

    phablet@ubuntu-phablet:~/Downloads$ tar -cvf moo.tar moo1/
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/
    moo1/moo2/moo3/
    moo1/moo2/moo3/moo4/
    moo1/moo2/moo3/moo4/moo5/
    phablet@ubuntu-phablet:~/Downloads$ tar -f moo.tar --delete "moo1/moo2/moo3"
    phablet@ubuntu-phablet:~/Downloads$ tar -tf moo.tar 
    moo1/
    moo1/moo2/

특정 파일 형식은에서 찾을 수 있습니다 tar -tf foo.tar|egrep -i '.jpg$'.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.