대용량 파일을 추가하여 보관하고 동시에 삭제하는 방법

대용량 파일을 추가하여 보관하고 동시에 삭제하는 방법

100GB 시스템에 80GB 파일이 있고 /root/bigfile이 파일을 아카이브에 저장하고 싶다고 가정해 보겠습니다./root/bigarchive.tar

분명히 아카이브에 파일을 추가하는 동안 파일을 삭제해야 합니다. 그래서 내 질문은 다음과 같습니다

아카이브에 파일을 추가하는 동안 어떻게 파일을 삭제할 수 있나요?

답변1

단일 파일 비압축 tar 아카이브는 헤더, 파일 및 트레일러로 구성됩니다. 따라서 주요 질문은 파일 시작 부분에 512바이트 헤더를 추가하는 방법입니다. 헤더만 사용하여 원하는 결과를 생성하는 것부터 시작할 수 있습니다.

tar cf - bigfile | dd count=1 >bigarchive.tar

그런 다음 파일의 처음 10G를 복사하십시오. 간단히 말해서, dd가 한 번에 1Gib를 읽고 쓸 수 있다고 가정해 보겠습니다.

dd count=10 bs=1G if=bigfile >>bigarchive.tar

이제 원본 파일에서 복사된 데이터를 릴리스합니다.

fallocate --punch-hole -o 0 -l 10GiB bigfile

이는 데이터를 다음으로 대체합니다.부족한파일 시스템 공간을 차지하지 않는 0입니다. 이 방식을 계속해서 skip=10다음 항목에 a를 추가한 dd다음 fallocate시작 오프셋을 로 늘립니다 -o 10GiB. 마지막으로 일부 null 문자를 추가하여 최종 tar 파일을 채웁니다.


파일 시스템이 이를 지원하지 않는 경우 fallocate유사한 작업을 수행할 수 있지만 파일 끝에서 시작하십시오. 먼저 파일의 마지막 10GB를 이름이 지정된 파일에 복사한 part8다음 명령을 사용하여 truncate원본 파일의 크기를 줄이세요. 각각 10Gibyte의 파일 8개가 생길 때까지 유사한 작업을 계속합니다. 그런 다음 헤더와 을 연결 part1bigarchive.tar다음 삭제 part1하고 연결하여 part2삭제하는 등의 작업을 수행할 수 있습니다.

답변2

파일을 삭제한다고 해서 반드시 생각한 대로 되는 것은 아닙니다. 이것이 UNIX와 유사한 시스템에서 시스템 호출이 호출되는 이유입니다.unlink설마 delete. 매뉴얼 페이지에서:

unlink() deletes a name from the filesystem.  If that name was the last
link to a file and no processes have the file open, the file is deleted
and the space it was using is made available for reuse.

If the name was the last link to a file but any processes still have
the file open, the file will remain in existence until  the  last  file
descriptor referring to it is closed.

따라서 데이터 압축기/아카이버가 파일에서 데이터를 읽는 동안 파일은 여전히 ​​존재하며 파일 시스템의 공간을 차지합니다.

답변3

아카이브에 파일을 추가하는 동안 어떻게 파일을 삭제할 수 있나요?

문맥을 고려하여 나는 질문을 다음과 같이 해석합니다.

디스크에서 데이터를 읽은 직후, 전체 파일을 읽기 전에 디스크에서 데이터를 삭제하여 변환된 파일을 위한 충분한 공간을 확보하려면 어떻게 해야 합니까?

변환은 압축, 암호화 등 데이터로 수행하려는 모든 작업이 될 수 있습니다.

대답은 다음과 같습니다.

<$file gzip | dd bs=$buffer iflag=fullblock of=$file conv=notrunc

간단히 말해서, 데이터를 읽고 gzip(또는 원하는 대로)에 넣고 출력을 버퍼링하여 쓴 것보다 읽은 것이 더 많은지 확인한 다음 다시 파일에 씁니다. 다음은 더 예쁜 버전이며 실행 시 출력을 보여줍니다.

cat "$file" \
| pv -cN 'bytes read from file' \
| gzip \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$file" conv=notrunc 2>/dev/null

한 줄씩 살펴 보겠습니다.

cat "$file"압축하려는 파일을 읽으십시오. 다음 부분 pv도 파일을 읽을 수 있기 때문에 이것은 cat(UUOC)의 쓸모없는 사용이지만, 나는 이것이 더 예쁘다고 생각합니다.

파이프를 통해 pv진행 정보를 표시합니다( -cN"어떤 종류의 [c]ursor를 사용하세요"라고 말하고 [N]이름을 지정합니다).

파이프는 gzip분명히 압축을 수행합니다(stdin에서 읽고 stdout으로 출력).

이 파이프는 다른 파이프에 연결되어 있습니다 pv(파이프라인 보기).

그 파이프는 에 들어갑니다 dd bs=$buffer iflag=fullblock. $buffer변수는 50MB와 같은 숫자입니다. 파일을 안전하게 처리하는 데 얼마나 많은 RAM을 할당하고 싶든 상관없습니다(데이터 포인트로는 2GB 파일에 대해 50MB 버퍼이면 충분합니다). iflag=fullblock파이프를 통과하기 전에 읽을 최대 바이트 수를 나타냅니다 dd. $buffer처음에 gzip은 헤더를 작성하므로 gzip의 출력은 이 dd줄에 표시됩니다. 그런 다음 dd입력을 더 읽을 수 있도록 파이프하기 전에 충분한 데이터가 있을 때까지 기다립니다. 또한 압축할 수 없는 부분이 있는 경우 출력 파일이 입력 파일보다 클 수 있습니다. 이 버퍼는 $buffer대부분의 바이트에서 이것이 문제가 되지 않도록 보장합니다.

그런 다음 다른 파이프라인 뷰 라인으로 이동하고 마지막으로 출력 dd라인으로 이동합니다. 쓰기 전에 출력 파일을 자르지(삭제)하지 않도록 지시하는 행 이 지정되었습니다 of(출력 파일) . 따라서 500바이트가 있고 3바이트를 썼다 면 파일은 (대신conv=notruncnotruncddABBBBAAAAA...교체됨통과 BBB).

나는 이 부분을 다루지 않았 2>/dev/null으며 불필요합니다. 그들은 단지 dd"완료했고 이 많은 바이트를 기록했습니다"라는 메시지를 억제하여 출력을 약간 정리할 뿐입니다. 각 줄 끝에 있는 백슬래시( )는 \bash가 전체 내용을 파이프로 서로 연결된 하나의 큰 명령으로 처리하도록 합니다.


이것은 사용 편의성을 위한 완전한 스크립트입니다. 흥미롭게도 "gz-in-place"라는 폴더에 넣었습니다. 그러다가 내가 만든 약어인 GZIP: gnu zip in-place를 깨달았습니다. 그래서 여기에 GZIP.sh를 소개합니다.

#!/usr/bin/env bash

### Settings

# Buffer is how many bytes to buffer before writing back to the original file.
# It is meant to prevent the gzip header from overwriting data, and in case
# there are parts that are uncompressible where the compressor might exceed
# the original filesize. In these cases, the buffer will help prevent damage.
buffer=$((1024*1024*50)) # 50 MiB

# You will need something that can work in stream mode from stdin to stdout.
compressor="gzip"

# For gzip, you might want to pass -9 for better compression. The default is
# (typically?) 6.
compressorargs=""

### End of settings

# FYI I'm aware of the UUOC but it's prettier this way

if [ $# -ne 1 ] || [ "x$1" == "x-h" ] || [ "x$1" == "x--help" ]; then
    cat << EOF
Usage: $0 filename
Where 'filename' is the file to compress in-place.

NO GUARANTEES ARE GIVEN THAT THIS WILL WORK!
Only operate on data that you have backups of.
(But you always back up important data anyway, right?)

See the source for more settings, such as buffer size (more is safer) and
compression level.

The only non-standard dependency is pv, though you could take it out
with no adverse effects, other than having no info about progress.
EOF
    exit 1;
fi;

b=$(($buffer/1024/1024));
echo "Progressing '$1' with ${b}MiB buffer...";
echo "Note: I have no means of detecting this, but if you see the 'bytes read from";
echo "file' exceed 'bytes written back to file', your file is now garbage.";
echo "";

cat "$1" \
| pv -cN 'bytes read from file' \
| $compressor $compressorargs \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$1" conv=notrunc 2>/dev/null

echo "Done!";

다른 버퍼 라인을 추가하고 싶습니다앞으로ddgzip을 사용하면 버퍼 라인이 플러시될 때 너무 멀리 쓰는 것을 방지할 수 있지만 50MiB 버퍼와 1900MB /dev/urandom의 데이터만 있으면 이미 작동하는 것 같습니다(압축 해제 후 md5sum이 일치함). 나에겐 충분히 좋은 비율이다.

또 다른 개선점은 글쓰기를 너무 멀리 감지하는 것이지만 사물의 아름다움을 앗아가고 많은 복잡성을 만들지 않고 그렇게 하는 방법을 모르겠습니다. 그 시점에서는 모든 것을 올바르게 수행하는 완전한 Python 프로그램으로 전환할 수도 있습니다(데이터 손상을 방지하기 위한 안전 장치 포함).

답변4

GNU 명령을 사용하는 경우 tar다음 옵션을 사용할 수 있습니다 --remove-files.

--파일 삭제

파일을 아카이브에 추가한 후 삭제

tar -cvf files.tar --remove-files my_directory

관련 정보