3천만 개의 작은 파일이 들어 있는 큰 폴더가 있습니다. 폴더를 30개의 아카이브로 백업하고 싶습니다. 각 tar.gz 파일에는 1M 파일이 있습니다. 여러 아카이브로 나누는 이유는 대용량 아카이브를 압축 해제하는 데 한 달이 걸리기 때문이다. 파일을 풀 때 모든 아카이브를 함께 넣어야 하기 때문에 분할할 파이핑 tar도 작동하지 않습니다.
또한, 나는 각 파일을 새 디렉토리로 이동시키지 않는 것을 선호합니다. 왜냐하면 이 거대한 폴더에는 ls조차도 매우 고통스러울 것이기 때문입니다.
답변1
이를 위해 이 bash 스크립트를 작성했습니다. 기본적으로 각 tar에 들어갈 파일 이름으로 배열을 형성한 tar
다음이것들은 모두 평행하다.. 이는 가장 효율적인 방법은 아닐 수 있지만 원하는 방식으로 작업을 완료할 수 있습니다. 하지만 메모리 소모가 많을 것으로 예상됩니다.
스크립트 시작 부분에서 옵션을 조정해야 합니다. cvjf
마지막 줄의 tar 옵션을 변경할 수도 있습니다 (예: v
성능 향상을 위해 자세한 출력을 제거하거나 압축을 다음 j
으로 변경하는 z
등).
스크립트
#!/bin/bash
# User configuratoin
#===================
files=(*.log) # Set the file pattern to be used, e.g. (*.txt) or (*)
num_files_per_tar=5 # Number of files per tar
num_procs=4 # Number of tar processes to start
tar_file_dir='/tmp' # Tar files dir
tar_file_name_prefix='tar' # prefix for tar file names
tar_file_name="$tar_file_dir/$tar_file_name_prefix"
# Main algorithm
#===============
num_tars=$((${#files[@]}/num_files_per_tar)) # the number of tar files to create
tar_files=() # will hold the names of files for each tar
tar_start=0 # gets update where each tar starts
# Loop over the files adding their names to be tared
for i in `seq 0 $((num_tars-1))`
do
tar_files[$i]="$tar_file_name$i.tar.bz2 ${files[@]:tar_start:num_files_per_tar}"
tar_start=$((tar_start+num_files_per_tar))
done
# Start tar in parallel for each of the strings we just constructed
printf '%s\n' "${tar_files[@]}" | xargs -n$((num_files_per_tar+1)) -P$num_procs tar cjvf
설명하다
먼저, 선택한 패턴과 일치하는 모든 파일 이름이 배열에 저장됩니다 files
. 다음으로 for 루프는 배열을 분할하고 해당 분할에서 문자열을 형성합니다. 슬라이스 수는 필요한 타르볼 수와 같습니다. 결과 문자열은 배열에 저장됩니다 tar_files
. for 루프는 또한 생성된 tarball의 이름을 각 문자열의 시작 부분에 추가합니다. 의 요소는 tar_files
다음과 같은 형식을 취합니다(5개의 파일/타르볼로 가정):
tar_files[0]="tar0.tar.bz2 file1 file2 file3 file4 file5"
tar_files[1]="tar1.tar.bz2 file6 file7 file8 file9 file10"
...
스크립트의 마지막 줄은 여러 프로세스(지정된 최대 수까지)를 xargs
시작하는 데 사용되며 , 각 프로세스는 배열의 한 요소를 병렬로 처리합니다.tar
tar_files
시험
파일 목록:
$ls
a c e g i k m n p r t
b d f h j l o q s
생성된 압축 패키지: $ls /tmp/tar* tar0.tar.bz2 tar1.tar.bz2 tar2.tar.bz2 tar3.tar.bz2
답변2
여기 또 다른 스크립트가 있습니다. 세그먼트당 정확히 1백만 개의 파일을 원하는지 아니면 정확히 30개의 세그먼트를 원하는지 선택할 수 있습니다. 이 스크립트에서는 전자를 선택했지만 split
키워드에서는 두 가지 중 하나를 선택할 수 있습니다.
#!/bin/bash
#
DIR="$1" # The source of the millions of files
TARDEST="$2" # Where the tarballs should be placed
# Create the million-file segments
rm -f /tmp/chunk.*
find "$DIR" -type f | split -l 1000000 - /tmp/chunk.
# Create corresponding tarballs
for CHUNK in $(cd /tmp && echo chunk.*)
do
test -f "$CHUNK" || continue
echo "Creating tarball for chunk '$CHUNK'" >&2
tar cTf "/tmp/$CHUNK" "$TARDEST/$CHUNK.tar"
rm -f "/tmp/$CHUNK"
done
이 스크립트에 적용할 수 있는 세부 사항이 많이 있습니다. 파일 목록 접두사로서의 사용은 /tmp/chunk.
아마도 상수 선언으로 푸시되어야 하며 코드는 일치하는 항목을 제거할 수 있다고 실제로 가정해서는 안 되지만 /tmp/chunk.*
, 나는 이것을 완전한 유틸리티가 아닌 개념 증명으로 유지하고 있습니다. 이것을 사용하면 mktemp
파일 목록을 보관할 임시 디렉토리를 만듭니다.
답변3
이것이 바로 요구되는 것입니다:
#!/bin/bash
ctr=0;
# Read 1M lines, strip newline chars, put the results into an array named "asdf"
while readarray -n 1000000 -t asdf; do
ctr=$((${ctr}+1));
# "${asdf[@]}" expands each entry in the array such that any special characters in
# the filename won't cause problems
tar czf /destination/path/asdf.${ctr}.tgz "${asdf[@]}";
# If you don't want compression, use this instead:
#tar cf /destination/path/asdf.${ctr}.tar "${asdf[@]}";
# this is the canonical way to generate output
# for consumption by read/readarray in bash
done <(find /source/path -not -type d);
readarray
(bash에서) 콜백 함수를 실행하는 데에도 사용할 수 있으므로 다음과 같이 다시 작성할 수 있습니다.
function something() {...}
find /source/path -not -type d \
| readarray -n 1000000 -t -C something asdf
GNU를 parallel
사용하여 비슷한 작업을 수행할 수 있습니다(테스트되지 않았습니다. 현재 있는 곳에 설치되어 있지 않으므로 parallel
그냥 사용하고 있습니다).
find /source/path -not -type d -print0 \
| parallel -j4 -d '\0' -N1000000 tar czf '/destination/path/thing_backup.{#}.tgz'
테스트되지 않았으므로 --dry-run
arg를 추가하여 실제로 수행되는 작업을 확인할 수 있습니다. 나는 이것이 가장 마음에 들지만 모든 사람이 parallel
이것을 설치하지는 않습니다. -j4
한 번에 4개의 작업을 사용하게 하며, -d '\0'
'와 결합하면 파일 이름의 특수 문자(공백 등)를 무시하게 됩니다. 나머지는 자명해야 합니다.find
-print0
다음과 같은 작업을 수행하는 것이 가능 parallel
하지만 임의의 파일 이름을 생성하기 때문에 마음에 들지 않습니다.
find /source/path -not -type d -print0 \
| parallel -j4 -d '\0' -N1000000 --tmpdir /destination/path --files tar cz
연속된 파일 이름을 생성하는 방법을 아직 모르겠습니다.
xargs
또한 작동하지만 parallel
출력 파일 이름을 생성하는 직접적인 방법이 없기 때문에 결국 다음과 같이 어리 석고 해커 같은 일을하게됩니다.
find /source/path -not -type d -print0 \
| xargs -P 4 -0 -L 1000000 bash -euc 'tar czf $(mktemp --suffix=".tgz" /destination/path/backup_XXX) "$@"'
OP는 분할을 사용하고 싶지 않다고 말했습니다. cat
다시 결합하면 괜찮을 것이기 때문에 이상해 보이는 것 같습니다. 그러면 tar가 생성되어 3GB 청크로 분할됩니다.
tar c /source/path | split -b $((3*1024*1024*1024)) - /destination/path/thing.tar.
...현재 디렉토리로 추출됩니다.
cat $(\ls -1 /destination/path/thing.tar.* | sort) | tar x
답변4
또 다른 스크립트가 있습니다:https://gist.github.com/s5unty/e636a1ca698c6817330825eba67941e7
1: /boot를 여러 tar 파일로 패키지화
$ tar -c -v --index-file=pack.index -M -L 10M -f /dev/null -F pack.sh /boot && pack.sh END
————
-rw-r--r-- 1 8.8K Sep 1 22:30 pack~1.index <-- file list
-rw-r--r-- 1 11M Sep 1 22:30 pack~1.tar <-- tar file (not a multiple-part, is a whole/pure tar)
-rw-r--r-- 1 116 Sep 1 22:30 pack~2.index
-rw-r--r-- 1 11M Sep 1 22:30 pack~2.tar
-rw-r--r-- 1 107 Sep 1 22:30 pack~3.index
-rw-r--r-- 1 13M Sep 1 22:30 pack~3.tar
-rw-r--r-- 1 102 Sep 1 22:30 pack~4.index
-rw-r--r-- 1 15M Sep 1 22:30 pack~4.tar <-- big tar file,
-rw-r--r-- 1 5.3M Sep 1 22:30 pack~4.tar.part2 <-- with second part
-rw-r--r-- 1 0 Sep 1 22:30 pack~5.index
-rw-r--r-- 1 10K Sep 1 22:30 pack~5.tar
-rw-r--r-- 1 0 Sep 1 22:30 pack~x.index <-- the last (~x)
-rw-r--r-- 1 10K Sep 1 22:30 pack~x.tar <-- the last (~x)
————
2: 단일 tar 파일의 압축을 푼다.
$ tar -x -v -f pack~1.tar
OR
$ tar -x -v -f pack~x.tar
3: 단일 tar 파일의 압축을 푼다(여러 부분 포함, BIG tar라고도 함)
$ tar -x -v -f pack~4.tar -F "pack.sh BIG"
4: 모든 tar 파일의 압축을 푼다
$ ls -1 *.tar | xargs -I% tar -F "pack.sh BIG" -xf %