Копирование большого количества файлов из одного каталога в другой в 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, но дельты - нет.

Другие вопросы по тегам