Answers:
这样一来就可以完成整个工作-在所有子目录中,都在一个流中,而没有任何文件名问题。它将从最小的文件复制到最大的每个文件。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
或者,由于Gilles在回答中保留目录权限非常重要,因此我也应该尝试。我认为这可以做到:
find . -type d -printf '[ -d "'"${DESTINATION}"'/%p" ] ||
cp "%p" -t "'"${DESTINATION}"'"\n' |
. /dev/stdin
我愿意打赌这比mkdir
任何时候都快。
这是使用的一种快速而肮脏的方法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.
当然,逐个文件传输的顺序并不是严格按照从小到大的顺序排列,但是我认为这可能是满足您要求的最简单的解决方案。
--copy-dest=DIR
和/或--compare-dest=DIR
我认为,您会做得更好。我只知道原因是我在发布自己的答案后不得不添加--hard-dereference
自己,tar
因为我缺少链接。我认为rsync
实际上与其他文件相比,本地文件系统的行为更具体-我以前将其与USB密钥一起使用,除非设置带宽限制,否则它将淹没总线。我想我应该改用其他任何一种。
不cp
直接,这远远超出了它的能力范围。但是您可以安排以cp
正确的顺序调用文件。
Zsh方便地允许使用glob限定符按大小对文件排序。这是一个zsh片段,该片段按从下/path/to/source-directory
到下的大小递增顺序复制文件/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 **/*(/e\''REPLY=/path/to/destination-directory/$REPLY'\')
zcp -Q '**/*(.oL)' '/path/to/destination-directory/$f'
这不会保留源目录的所有权。如果需要,您需要注册一个合适的复制程序,例如cpio
或pax
。如果您这样做,则无需致电cp
或zcp
额外付费。
cd /path/to/source-directory
print -rN **/*(^.) **/*(.oL) | cpio -0 -p /path/to/destination-directory
我认为没有任何方法可以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
请注意,这些文件的大小按升序排列。
第cp
34行上的命令是一个文字shell命令,因此您可以使用开关进行任何操作(我只是用来-a
保留所有特征)。
File::Find
并且File::Basename
都是核心模块,即它们在perl的所有安装中都可用。
cp - copy smallest files first?
但帖子的标题只是“ copy smallest files first?
反正”,选择永远不会受到伤害是我的理念,但是,您和David是唯一使用过的人,cp
而您是唯一将其发布的人。
cp
是因为它是在(面向跨平台的)perl中保留* nix文件特征的最简单方法。浏览器栏上显示的原因cp -
是(IMO高飞)SE功能所致,在该功能中,最流行的所选标签显示在实际标题的前面。
pearl
在这里周围很少看到木制品出来的东西。
另一个选择是将cp与du的输出一起使用:
oldIFS=$IFS
IFS=''
for i in $(du -sk *mpg | sort -n | cut -f 2)
do
cp $i destination
done
IFS=$oldIFS
仍然可以在一行上完成此操作,但我将其拆分,以便您可以阅读