Я только что задал вопрос , связанный с тем, как я могу считать файлы определенного расширения. Теперь я хочу cp
эти файлы в новый dir
.
Я пытаюсь,
cp *.prj ../prjshp/
и
cp * | grep '\.prj , но они дают ту же ошибку,
bash: / bin / cp: Слишком длинный список аргументов
blockquote> Как их скопировать?
../prjshp/
, но они дают ту же ошибку,
bash: / bin / cp: Слишком длинный список аргументов
blockquote>Как их скопировать?
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
в подкаталогах не будут найдены). ИМХО, оптимальные инструменты для работы с полчищами файлов - это найти
и xargs
. См. man find
. См. man xargs
. find
с его переключателем -print0
создает список имен файлов, разделенных NUL
(имена файлов могут содержать любой символ execpt NUL
или /
), который понимает xargs
, с помощью переключателя -0
. xargs
затем создает самую длинную разрешенную команду (наибольшее количество имен файлов, без половины имени файла в конце) и выполняет ее. xargs
повторяет это, пока find
не перестанет предоставлять имена файлов. Запустите xargs --show-limits dev / null
, чтобы увидеть пределы.
Для решения вашей проблемы (и после проверки man cp
, чтобы найти - target-directory =
):
find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
Эта команда копирует файлы один за другим и будет работать, даже если их слишком много. их для *
для расширения в одну команду cp
:
for i in *; do cp "$i" ../prjshp/; done
Есть 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 .