8

У меня есть каталог, содержащий около 280 000 файлов. Я хочу переместить их в другой каталог.

Если я использую cp или mv то получаю ошибку «список аргументов слишком длинный».

Если я напишу сценарий, как

for file in ls *; do
   cp {source} to {destination} 
done

затем из-за команды ls ее производительность ухудшается.

Как я могу это сделать?

9 ответов9

16

Используйте rsync:

$ rsync -a {source}/ {destination}/

например

$ rsync -a /some/path/to/src/ /other/path/to/dest/

(обратите внимание на трейлинг / с)


Примечание: если это длительная операция, и вы хотите увидеть некоторые признаки прогресса во время копирования, вы можете либо добавить опцию -v (подробный), которая затем перечисляет каждый копируемый файл, либо рассмотреть возможность использования опции --progress , для большей краткости вывод результатов.
8

Я пропускаю два ответа в ответах, поэтому добавляю еще один.

Хотя это напоминает мне о добавлении еще одного стандартного ответа ...

Здесь есть две проблемы:

У меня есть каталог, содержащий около 280 000 файлов.

Большинство инструментов не так хорошо масштабируются с таким количеством файлов. Не только большинство инструментов для 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 аргументами. Это более эффективно (читай: быстрее).

1

Вам не нужен ls, вы можете просто использовать

for file in *; do
    cp $file /your/dest
done

или вы можете сделать что-то вроде:

echo * | xargs -i cp {} /your/dest
0

Используя tar:

(cd {origin}; tar cf - .)|(cd {destination}; tar xvf -)

Работает, чтобы начать все, когда источник изначально слишком велик для rsync, но дельты - нет.

0

Предполагая, что вы хотите переместить файлы в пределах одной и той же файловой системы, вы можете просто переименовать каталог, содержащий ваши lac, и покончить с этим.

0

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

0

Как насчет перемещения (вместо копирования):

$ find {origin}/ -maxdepth 1 -name "*" -o -name ".*" -exec mv '{}'  {destination}/ ';'

Я думаю, что он будет перемещаться, сохраняя структуру (подкаталоги) и скрытые файлы или каталоги, плюс дополнительное пространство не будет использовано, как с rsync + rm. И если {origin} и {destination} находятся в одном разделе, это будет быстрее.

0
#!/bin/bash
d=$(date +%Y%m%d%H%m%s)
cd /path
tar zcvf "/destination/bakup_${d}.tar.gz" mydirectory_for_transer
0

Мне нравится rsync для этого или:

find dir1 -type f -exec cp {} dir2 \;

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .