У меня есть большой каталог, содержащий подкаталоги и файлы, которые я хочу копировать рекурсивно.
Есть ли способ сказать, 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
Это все еще можно сделать в одной строке, но я разделил его, чтобы вы могли прочитать его