Копирование большого количества файлов из одного каталога в другой в Linux
У меня есть каталог, содержащий около 280 000 файлов. Я хочу переместить их в другой каталог.
Если я использую cp
или же mv
тогда я получаю ошибку "список аргументов слишком длинный".
Если я напишу сценарий, как
for file in ls *; do
cp {source} to {destination}
done
затем из-за ls
команда, ее производительность ухудшается.
Как я могу это сделать?
9 ответов
Используйте rsync:
$ rsync -a {source}/ {destination}/
например
$ rsync -a /some/path/to/src/ /other/path/to/dest/
(обратите внимание на трейлинг / с)
Примечание: если это длительная операция, и вы хотите увидеть некоторые признаки прогресса во время копирования, вы можете добавить
-v
(подробный), который затем перечисляет каждый копируемый файл, или рассмотрите возможность использования --progress
опция, для более краткого вывода результатов.
Я пропускаю два ответа в ответах, поэтому добавляю еще один.
Хотя это напоминает мне о добавлении еще одного стандартного ответа...
Здесь есть две проблемы:
У меня есть каталог, содержащий около 280000 файлов.
Большинство инструментов не так хорошо масштабируются с таким количеством файлов. Не только большинство инструментов для Linux или Windows, но и довольно много программ. И это может включать в себя вашу файловую систему. Долгосрочное решение было бы "ну, не делай этого тогда". Если у вас разные файлы, но они в разных каталогах. Если не ожидайте, что продолжите сталкиваться с проблемами в будущем.
Сказав это, давайте перейдем к вашей актуальной проблеме:
Если я использую cp или mv, то получаю ошибку "список аргументов слишком длинный"
Это вызвано расширением * оболочкой. Оболочка имеет ограниченное пространство для результата, и она заканчивается. Это означает любую команду с *
расширенная оболочка столкнется с той же проблемой. Вам нужно будет одновременно расширить меньше параметров или использовать другую команду.
Одна альтернативная команда часто используется, когда вы сталкиваетесь с этой проблемой: find
, Уже есть несколько ответов, показывающих, как его использовать, поэтому я не собираюсь повторять все это. Однако я собираюсь указать на разницу между \;
а также +
, так как это может иметь огромное значение для производительности и хорошо вписаться в предыдущее объяснение расширения.
find /path/to/search --name "*.txt" -exec command {} \;
Найдет все файлы по пути / to / search / и выполнит команду с ним, но заметит кавычки вокруг *
, Это кормит * для команды. Если бы мы не инкапсулировали или не экранировали его, то оболочка попыталась бы расширить его, и мы получили бы ту же ошибку.
Наконец, я хочу упомянуть кое-что о {}. Эти скобки заменяются содержимым, найденным функцией find. Если вы заканчиваете команду точкой с запятой ;
(тот, который вам нужно убежать из оболочки, следовательно, \;
в примерах), то результаты передаются по одному. Это означает, что вы будете выполнять 280000 команд mv. Один для каждого файла. Это может быть медленно.
В качестве альтернативы вы можете закончить +
, Это передаст столько аргументов, сколько возможно одновременно. Если bash может обработать 2000 аргументов, команда find /path -name "*filetype" -exec some_move {}+ вызовет команду some_move примерно 140 раз, каждый раз с 2000 аргументами. Это более эффективно (читай: быстрее).
Как насчет перемещения (вместо копирования):
$ find {origin}/ -maxdepth 1 -name "*" -o -name ".*" -exec mv '{}' {destination}/ ';'
Я думаю, что он будет перемещаться, сохраняя структуру (подкаталоги) и скрытые файлы или каталоги, плюс дополнительное пространство не будет использовано, как с rsync + rm. И если {origin} и {destination} находятся в одном разделе, это будет быстрее.
Вам не нужен ls, вы можете просто использовать
for file in *; do
cp $file /your/dest
done
или вы можете сделать что-то вроде:
echo * | xargs -i cp {} /your/dest
В моем случае оба cp
а также rsync
было слишком медленно копировать около 4 миллионов файлов с жесткого диска на SSD, так что вот как я это сделал (все мои файлы были.txt файлами в одной папке, поэтому настройте find
подойдет вам):
cd /path/to/source/folder
find . -name '*.txt' -print >/tmp/test.manifest
tar -c -T /tmp/test.manifest | (cd /path/to/destination/folder; tar xfp -)
Я должен был напечатать имена файлов во временный файл, потому что я нажал Argument list too long
ошибка. С помощью tar
значительно улучшил мою скорость передачи, хотя я могу предположить, что файлы, которые менее легко сжимаются, могут работать не так хорошо.
Мне нравится rsync
для этого или:
find dir1 -type f -exec cp {} dir2 \;
#!/bin/bash
d=$(date +%Y%m%d%H%m%s)
cd /path
tar zcvf "/destination/bakup_${d}.tar.gz" mydirectory_for_transer
Предполагая, что вы хотите переместить файлы в пределах одной файловой системы, вы можете просто переименовать каталог, содержащий ваши lac, и покончить с этим.
Используя tar:
(cd {origin}; tar cf - .)|(cd {destination}; tar xvf -)
Работает, чтобы начать все, когда источник изначально слишком велик для rsync, но дельты - нет.