首先复制最小的文件?

首先复制最小的文件?

我有一个大目录,其中包含我希望递归复制的子目录和文件。

有什么方法可以告诉cp它应该按文件大小的顺序执行复制操作,以便首先复制最小的文件?

答案1

这是一种快速但肮脏的方法,使用rsync.对于这个例子,我认为 10 MB 以下的任何内容都是“小”的。

首先只传输小文件:

rsync -a --max-size=10m srcdir dstdir

然后传输剩余的文件。之前传输的小文件不会被重新复制,除非被修改。

rsync -a srcdir dstdir

man 1 rsync

   --max-size=SIZE
          This  tells  rsync to avoid transferring any file that is larger
          than the specified SIZE. The SIZE value can be suffixed  with  a
          string  to  indicate  a size multiplier, and may be a fractional
          value (e.g. "--max-size=1.5m").

          This option is a transfer rule, not an exclude,  so  it  doesn’t
          affect  the  data  that  goes  into  the file-lists, and thus it
          doesn’t affect deletions.  It just limits  the  files  that  the
          receiver requests to be transferred.

          The  suffixes  are  as  follows:  "K"  (or  "KiB") is a kibibyte
          (1024), "M" (or "MiB") is a mebibyte (1024*1024),  and  "G"  (or
          "GiB")  is  a gibibyte (1024*1024*1024).  If you want the multi‐
          plier to be 1000 instead of  1024,  use  "KB",  "MB",  or  "GB".
          (Note: lower-case is also accepted for all values.)  Finally, if
          the suffix ends in either "+1" or "-1", the value will be offset
          by one byte in the indicated direction.

          Examples:    --max-size=1.5mb-1    is    1499999    bytes,   and
          --max-size=2g+1 is 2147483649 bytes.

当然,逐个文件传输的顺序并不严格是从小到大,但我认为这可能是满足您的要求精神的最简单的解决方案。

答案2

这一次完成了整个工作 - 在所有子目录中,全部在单个流中,没有任何文件名问题。它将从小到大复制您拥有的每个文件。mkdir ${DESTINATION}如果它尚不存在,您将需要这样做。

find . ! -type d -print0 |
du -b0 --files0-from=/dev/stdin |
sort -zk1,1n | 
sed -zn 's/^[^0-9]*[0-9]*[^.]*//p' |
tar --hard-dereference --null -T /dev/stdin -cf - |
    tar -C"${DESTINATION}" --same-order -xvf -

但你知道吗?这不做的是空的子目录。我可以对该管道进行一些重定向,但这只是等待发生的竞争条件。最简单的可能是最好的。所以之后就这样做:

find . -type d -printf 'mkdir -p "'"${DESTINATION}"'/%p"\n' |
    . /dev/stdin

或者,由于吉尔斯在他的回答中提出了一个很好的观点,即保留目录权限,我也应该尝试一下。我认为这会做到这一点:

find . -type d -printf '[ -d "'"${DESTINATION}"'/%p" ] || 
    cp "%p" -t "'"${DESTINATION}"'"\n' |
. /dev/stdin

我愿意打赌这比mkdir无论如何都要快。

答案3

不是cp直接的,这远远超出了它的能力范围。但您可以安排cp以正确的顺序调用这些文件。

Zsh 方便地允许按大小对文件进行排序全局限定符。这是一个 zsh 片段,它按照大小递增的顺序从 under/path/to/source-directory到 under复制文件/path/to/destination-directory

cd /path/to/source-directory
for x in **/*(.oL); do
  mkdir -p /path/to/destination-directory/$x:h &&
    cp -- $x /path/to/destination-directory/$x:h
done

您可以使用zcp功能。但是,您需要首先创建目标目录,这可以在神秘的 oneliner 中完成。

autoload -U zmv; alias zcp='zmv -C'
cd /path/to/source-directory
mkdir -p **/*(/e\''REPLY=/path/to/destination-directory/$REPLY'\')
zcp -Q '**/*(.oL)' '/path/to/destination-directory/$f'

这不会保留源目录的所有权。如果您想要这样做,您需要使用合适的复制程序,例如cpiopax。如果您这样做,则无需另外调用cp或。zcp

cd /path/to/source-directory
print -rN -- **/*(^.) **/*(.oL) | cpio -0 -p /path/to/destination-directory

答案4

我认为没有任何方法可以cp -r直接做到这一点。由于在获得神奇find/解决方案之前可能需要一段不确定的时间awk,因此这里有一个快速的 Perl 脚本:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

use File::Find;
use File::Basename;

die "No (valid) source directory path given.\n"
    if (!$ARGV[0] || !-d -r "/$ARGV[0]");

die "No (valid) destination directory path given.\n"
    if (!$ARGV[1] || !-d -w "/$ARGV[1]");

my $len = length($ARGV[0]);
my @files;
find (
    sub {
        my $fpath = $File::Find::name;
        return if !-r -f $fpath;
        push @files, [
            substr($fpath, $len),
            (stat($fpath))[7],
        ]
    }, $ARGV[0]
);

foreach (sort { $a->[1] <=> $b->[1] } @files) {
    if ($ARGV[2]) {
        print "$_->[1] $ARGV[0]/$_->[0] -> $ARGV[1]/$_->[0]\n";
    } else {
        my $dest = "$ARGV[1]/$_->[0]";
        my $dir = dirname($dest);
        mkdir $dir if !-e $dir;
        `cp -a "$ARGV[0]/$_->[0]" $dest`;
    }
} 
  • 用这个:./whatever.pl /src/path /dest/path

  • 参数应该都是绝对路径; ~,或者 shell 扩展为绝对路径的任何其他内容都可以。

  • 如果您添加第三个参数(任何东西,除了文字0),它不会复制它,而是打印到标准输出它将做什么的报告,并以字节为单位预先考虑文件大小,例如

    4523 /src/path/file.x -> /dest/path/file.x
    12124 /src/path/file.z -> /dest/path/file.z
    

    请注意,这些是按大小升序排列的。

  • 第 34 行的命令cp是一个字面 shell 命令,因此您可以使用开关执行任何您想要的操作(我只是用来-a保留所有特征)。

  • File::FindFile::Basename都是核心模块,即它们在所有 perl 安装中都可用。

相关内容