Unix:如何仅压缩每个文件夹的前 N ​​个文件?

Unix:如何仅压缩每个文件夹的前 N ​​个文件?

我有一个包含 2Gb 图像的文件夹,其中有几层深的子文件夹。

我想仅将N每个(子)文件夹的文件存档在 tar 文件中。我尝试使用find当时tailtar无法让它工作。这是我尝试过的(假设N = 10):

find . | tail -n 10 | tar -czvf backup.tar.gz

...输出此错误:

Cannot stat: File name too long

这是怎么回事?想一想 - 即使它有效,我认为它只会压缩所有文件夹的前 10 个文件,而不是 10 个文件每个文件夹。

如何获取N每个文件夹的文件? (无需文件订单)

答案1

如果您pax支持该-0选项,请使用zsh

print -rN dir/**/*(D/e:'reply=($REPLY/*(ND^/[1,10]))':) |
  pax -w0 | xz > file.tar.xz

它包括列表中按文件名排序的每个目录的前 10 个非目录文件。您可以通过添加 glob 限定符来选择不同的排序顺序om(按修改时间排序,Om以反转顺序)、oL(按长度排序)、non(按名称排序但按数字排序)...

如果您没有标准pax命令,或者不支持-0但您有 GNUtar命令,您可以执行以下操作:

print -rN -- dir/**/*(D/e:'reply=($REPLY/*(ND^/[1,10]))':) |
  tar --null -T - -cjf file.tar.xz

如果您无法使用zsh,但可以访问bash(GNU 项目的 shell),您可以这样做:

find dir -type d -exec bash -O nullglob -O dotglob -c '
  for dir do
    set -- "$dir/*"; n=0
    for file do
      if [ ! -d "$file" ] || [ -L "$file" ]; then
        printf "%s\0" "$file"
        (( n++ < 10 )) || break
      fi
    done
  done' bash {} + | pax -0w | xz > file.tar.xz

但这会大大降低效率。

答案2

假设您的主目录/tmp/dir中您只想将其下每个(子)文件夹的 N 个(例如 N=10)个文件归档到一个backup.tar.gz文件中。

示例:tree/tmp/dir

dir/                                                                                                                                                                                                           
├── one
│   ├── one10.txt
│   ├── one11.txt
│   ├── one1.txt
│   ├── one2.txt
│   ├── one3.txt
│   ├── one4.txt
│   ├── one5.txt
│   ├── one6.txt
│   ├── one7.txt
│   ├── one8.txt
│   ├── one9.txt
│   └── one_deep
│       ├── one_deep1
│       ├── one_deep10
│       ├── one_deep11
│       ├── one_deep2
│       ├── one_deep3
│       ├── one_deep4
│       ├── one_deep5
│       ├── one_deep6
│       ├── one_deep7
│       ├── one_deep8
│       └── one_deep9
├── three
│   ├── three10.txt
│   ├── three11.txt
│   ├── three1.txt
│   ├── three2.txt
│   ├── three3.txt
│   ├── three4.txt
│   ├── three5.txt
│   ├── three6.txt
│   ├── three7.txt
│   ├── three8.txt
│   ├── three9.txt
│   └── three_deep
│       ├── three_deep1
│       ├── three_deep10
│       ├── three_deep11
│       ├── three_deep2
│       ├── three_deep3
│       ├── three_deep4
│       ├── three_deep5
│       ├── three_deep6
│       ├── three_deep7
│       ├── three_deep8
│       └── three_deep9

代码:

cd /tmp; for i in `find dir/* -type d`; do find $i -maxdepth 1 -type f | tail -n 10 | xargs -I file tar -rf backup.tar file; done; gzip backup.tar

这将创建一个backup.tar.gz包含 10 个文件的子文件夹/tmp/dir

答案3

由于 的输出find是扁平的,因此如果不查看路径,您实际上无法知道哪些文件属于同一目录。另一种方法是使用多个finds(每个文件夹一个),而无需查看路径。这就是我所做的。为了压缩每个子文件夹最多 10 个文件,请使用如下内容:

for dir in $(find . -type d); do
  find "$dir" -maxdepth 1 -type f -printf "\"%p\"\n" | tail -10
done | xargs tar cvfz backup.tar.gz

这会递归查找当前文件夹中的所有目录。对于每个目录,它最多查找 10 个文件确切地该文件夹 ( -maxdepth 1)。整个循环完成后,tar将对循环输出的所有文件执行该命令。我还通过引用$dirfind使用该选项打印引号内的每个文件名来计算带有空格的目录和文件夹名称-printf

答案4

对目录名使用哈希,并且仅在哈希值计数低于阈值时才发出文件名。例如

find . -depth -type f \
| perl -MFile::Spec -nle '(undef,$d,$f)=File::Spec->splitpath($_); print if $seen{$d}++ < 3' \
| tar ...

相关内容