Слишком длинный список аргументов при копировании файлов

Я только что задал вопрос , связанный с тем, как я могу считать файлы определенного расширения. Теперь я хочу cp эти файлы в новый dir.

Я пытаюсь,

cp *.prj ../prjshp/

и

cp * | grep '\.prj 

, но они дают ту же ошибку,

bash: / bin / cp: Слишком длинный список аргументов

Как их скопировать?

../prjshp/

, но они дают ту же ошибку,

bash: / bin / cp: Слишком длинный список аргументов

Как их скопировать?

37
задан 13 April 2017 в 15:23

4 ответа

cp * .prj ../prjshp/ - правильная команда, но вы столкнулись с редким случаем, когда она сталкивается с ограничением размера. Вторая команда, которую вы попробовали, не имеет никакого смысла.

Один из методов - запустить cp для файлов в кусках. Команда find знает, как это сделать:

find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
  • find рекурсивно просматривает текущий каталог и каталоги под ним.
  • -maxdepth 1 означает остановку на глубине 1 , т.е. не рекурсивно переходить в подкаталоги.
  • -name '* .prj' означает действовать только с файлами, имя которых соответствует указанному шаблону. Обратите внимание на кавычки вокруг рисунка:он будет интерпретирован командой find , а не оболочкой.
  • -exec… {} + означает выполнение указанной команды для всех файлов. При необходимости он вызывает команду несколько раз, стараясь не превысить лимит командной строки.
  • mv -t ../prjshp перемещает указанные файлы в ../ prjshp . Параметр -t используется здесь из-за ограничения команды find : найденные файлы (обозначенные символом {} ) передаются в качестве последнего аргумента функции команды, вы не можете добавить место назначения после нее.

Другой способ - использовать rsync .

rsync -r --include='*.prj' --exclude='*' . ../prjshp
  • rsync -r…. ../prjshp рекурсивно копирует текущий каталог в ../ prjshp .
  • - include = '*. prj' --exclude = '*' означает копирование файлов сопоставление * .prj и исключение всего остального (включая подкаталоги, поэтому файлы .prj в подкаталогах не будут найдены).
47
ответ дан 13 April 2017 в 15:23

ИМХО, оптимальные инструменты для работы с полчищами файлов - это найти и xargs . См. man find . См. man xargs . find с его переключателем -print0 создает список имен файлов, разделенных NUL (имена файлов могут содержать любой символ execpt NUL или / ), который понимает xargs , с помощью переключателя -0 . xargs затем создает самую длинную разрешенную команду (наибольшее количество имен файлов, без половины имени файла в конце) и выполняет ее. xargs повторяет это, пока find не перестанет предоставлять имена файлов. Запустите xargs --show-limits , чтобы увидеть пределы.

Для решения вашей проблемы (и после проверки man cp , чтобы найти - target-directory = ):

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
2
ответ дан 13 April 2017 в 15:23

Эта команда копирует файлы один за другим и будет работать, даже если их слишком много. их для * для расширения в одну команду cp :

for i in *; do cp "$i" ../prjshp/; done
30
ответ дан 13 April 2017 в 15:23

Есть 3 ключевых момента, о которых следует помнить, когда вы сталкиваетесь с Список аргументов слишком длинный ошибка:

  • Длина аргументов командной строки ограничена переменной ARG_MAX , которая согласно определению POSIX составляет «... [м] максимальная длина аргумента для функции exec , включая данные среды "(выделено)". То есть, когда оболочка выполняет команду, не являющуюся встроенной, она должна вызвать одну из exec () для создания этот процесс команды, и здесь ARG_MAX вступает в игру. Кроме того, играет роль имя или путь к самой команде (например, / bin / echo ).

  • Встроенные команды оболочки выполняются оболочкой, что означает, что оболочка не использует семейство функций exec () и, следовательно, не зависит от переменной ARG_MAX . [12 61] Определенные команды, такие как xargs и find , знают о переменной ARG_MAX и многократно выполняют действия ниже этого предела

Из пунктов выше и как показано в отличном ответе Кусалананды на связанный вопрос, слишком длинный список аргументов также может возникать при большой среде. Поэтому, принимая во внимание, что среда каждого пользователя может быть разной, и размер аргумента в байтах имеет значение, трудно придумать единое количество файлов / аргументов.

Как справиться с такой ошибкой?

Главное - сосредоточиться не на количестве файлов, а на том, включает ли команда, которую вы собираетесь использовать, семейство exec () функции и по касательной - пространство стека.

Используйте встроенные модули оболочки

Как обсуждалось ранее, встроенные модули оболочки невосприимчивы к пределу ARG_MAX , то есть таким вещам, как для Цикл , цикл while , встроенный echo и встроенный printf - все они работают достаточно хорошо.

for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done

На связанный с вопрос об удалении файлов было решение как таковое:

printf '%s\0' *.jpg | xargs -0 rm --

Обратите внимание, что здесь используется встроенный в оболочку printf . Если мы вызываем внешний printf , это будет включать exec () , следовательно, произойдет сбой с большим количеством аргументов:

$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long

массивы bash

Согласно ответ от jlliagre, bash не налагает ограничений на массивы, поэтому можно также построить массив имен файлов и использовать срезы для каждой итерации цикла, как показано в ответе danjpreron :

files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do 
    cp -t /path/to/new_dir/ "${files[@]:I:1000}" 
done

Это, однако, ограничивается тем, что оно специфично для bash и не относится к POSIX.

Увеличить пространство стека

Иногда можно увидеть, что люди предлагают увеличить пространство стека с помощью ] ulimit -s ; в Linux значение ARG_MAX составляет 1/4 пространства стека для каждой программы, что означает, что увеличение пространства стека пропорционально увеличивает пространство для аргументов.

# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $((  $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304

Согласно ответу Франка Дернонкура , цитирующего Linux Journal, можно также перекомпилировать ядро ​​Linux с большим значением максимального количества страниц памяти для аргументов, однако это больше работы, чем необходимо, и открывает потенциал для эксплойтов, как указано в цитируемой статье Linux Journal.

Избегайте оболочки

Другой способ - использовать python или python3 , которые по умолчанию поставляются с Ubuntu. Пример python + here-doc , приведенный ниже, - это то, что я лично использовал для копирования большого каталога файлов где-то в диапазоне 40 000 элементов:

$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
>    if os.path.isfile(f):
>         shutil.copy(f,'./newdir/')
> EOF

Для рекурсивного обхода вы можете использовать os. walk .

См. также:

7
ответ дан 13 April 2017 в 15:23

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

Похожие вопросы: