У меня есть большой каталог, содержащий подкаталоги и файлы, которые я хочу копировать рекурсивно.
Есть ли способ сказать, cpчто он должен выполнять операцию копирования в порядке размера файла, чтобы сначала копировать самые маленькие файлы?
У меня есть большой каталог, содержащий подкаталоги и файлы, которые я хочу копировать рекурсивно.
Есть ли способ сказать, cpчто он должен выполнять операцию копирования в порядке размера файла, чтобы сначала копировать самые маленькие файлы?
Ответы:
Это делает всю работу за один раз - во всех дочерних каталогах, все в одном потоке без каких-либо проблем с именами файлов. Он будет копировать от самого маленького до самого большого файла, который у вас есть. Вам нужно будет, 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любом случае.
Вот быстрый и грязный метод с использованием rsync . Для этого примера я рассматриваю что-нибудь под 10 МБ, чтобы быть "маленьким".
Сначала передайте только небольшие файлы:
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функцию. Однако сначала нужно создать каталоги назначения, что можно сделать в загадочном документе.
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
Оба аргумента должны быть абсолютными путями ; ~или что-либо еще, что расширяет оболочку по абсолютному пути, это нормально.
Если вы добавите третий аргумент (что угодно, кроме литерала 0), вместо его копирования будет распечатан стандартный отчет о том, что он будет делать, с предварительно добавленными размерами файлов, например
4523 /src/path/file.x -> /dest/path/file.x
12124 /src/path/file.z -> /dest/path/file.z
Обратите внимание, что они в порядке возрастания по размеру.
Команда cpв строке 34 является буквальной командой оболочки, так что вы можете делать с переключателями все, что захотите (я просто использовал -aдля сохранения всех характеристик).
File::Findи File::Basenameоба являются основными модулями, т.е. они доступны во всех установках perl.
cp - copy smallest files first?но название поста просто В copy smallest files first?любом случае, варианты никогда не повредят - это моя философия, но, тем не менее, вы и Дэвид - единственные, кто их использовал, cpи вы единственный, кто их выполнил.
cpзаключалась в том, что это самый простой способ сохранить характеристики файла * nix в (кроссплатформенном) Perl. Причина, по которой ваш браузер говорит, 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
Это все еще можно сделать в одной строке, но я разделил его, чтобы вы могли прочитать его