我有一个大文件夹,里面有30M的小文件。我希望将文件夹备份成30个档案,每个tar.gz文件有1M文件。分割成多个档案的原因是,解压一个大档案将需要一个月的时间。管道 tar 来分割也不起作用,因为当解压文件时,我必须将所有档案放在一起。
另外,我希望不要将每个文件 mv 到一个新目录,因为即使 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 循环对该数组进行切片并从切片形成字符串。切片的数量等于所需 tarball 的数量。结果字符串存储在数组中tar_files
。 for 循环还将生成的 tarball 的名称添加到每个字符串的开头。的元素tar_files
采用以下形式(假设 5 个文件/tarball):
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
这是另一个脚本。您可以选择是否需要每个段正好 100 万个文件,或者正好 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
GNUparallel
可以用来做类似的事情(未经测试;我没有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 说他们不想使用 split ...我认为这看起来很奇怪,因为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 %