Список аргументів занадто довгий під час копіювання файлів


26

Я щойно задав питання, пов’язане з тим, як я можу рахувати файли певного розширення. Тепер я хочу, щоб cpці файли були новими dir.

Я намагаюся,

cp *.prj ../prjshp/

і

cp * | grep '\.prj$' ../prjshp/

але вони дають ту саму помилку,

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

Як їх скопіювати?


Відповіді:


36

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файли в підкаталогах не знайдуться).

3
rsync, на сьогоднішній день найпростіше рішення.
ntk4

Щоб бути дещо непомітним, друга команда cp * | grep '\.prj$' ../prjshp/ не має жодного сенсу, але може бути синтаксично дійсною, якщо *розширюється до списку файлів, останній - каталог (aka cp SOURCE1 SOURCE2....DEST). Труба не має жодного сенсу, звичайно, але також залишається синтаксично дійсною, що стосується оболонки - це буде dup()дескрипторами файлів просто чудово, це просто те, що читацький кінець труби не отримає жодних даних, оскільки cpне записує жодних .
Сергій Колодяжний

І знаходження, і rsync створили один і той же список аргументів для мене занадто довгу помилку. Цикл for був найпростішим рішенням.
Meezaan-ud-Din

Дійсно, rsync - це спосіб зробити будь-яке масове копіювання, хоча я натрапив на те, наскільки ми дійшли з Linux, і у нас є дурний недолік / помилка, як це, і так, я вважаю це недоліком / помилкою.
MitchellK

22

Ця команда копіює файли один за одним і буде працювати, навіть якщо їх занадто багато, *щоб розширитись в одну cpкоманду:

for i in *; do cp "$i" ../prjshp/; done

Це працює для мене.
1rq3fea324 було

1
Простий і ефективний. У мене була схожа проблема з видаленням ~ 1/4 мільйона jpegs, які я вилучив із відео для проекту. Це такий підхід, який я використовував.
Старійшина Гік

5

Майте на увазі 3 ключові моменти, які потрібно пам’ятати, коли стикаєтеся з Argument list too longпомилкою:

  • Довжина аргументів командного рядка обмежена ARG_MAXзмінною, яка за визначенням POSIX - "... [m] осева довжина аргументу до функцій exec, включаючи дані середовища" (наголос додано) ". Тобто, коли оболонка виконує не Команда -built-it, вона повинна викликати одного з, exec()щоб нерестувати процес цієї команди, і ось тут ARG_MAXвступає в дію.Додатково, ім'я або шлях до самої команди (наприклад, /bin/echo) грає роль.

  • Команди, що вбудовуються в оболонки, виконуються оболонками, що означає, що оболонка не використовує exec()сімейство функцій і тому ARG_MAXзмінна не впливає .

  • Деякі команди, такі як xargsі findзнають про ARG_MAXзмінну, і неодноразово виконують дії під цією межею

З наведених вище пунктів і, як показано в чудовій відповіді Кусалаланда на відповідне питання, це Argument list too longможе статися і тоді, коли середовище велике. Отже, враховуючи, що середовище кожного користувача може відрізнятися, а розмір аргументу в байтах є релевантним, важко придумати єдину кількість файлів / аргументів.

Як впоратися з такою помилкою?

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

Використовуйте вбудовані оболонки

Як обговорювалося раніше, вбудовані оболонки захищені від ARG_MAXобмеження, тобто такі речі, як forцикл, 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 <NUM>; для 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або python3Ubuntu за замовчуванням. Приклад 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 .

Дивись також:


2

IMHO, оптимальними інструментами для роботи з ордами файлів є findі xargs. Див man find. Див man xargs. find, за допомогою свого -print0перемикача створює NULрозділений список імен файлів (імена файлів можуть містити будь-який витяг символів 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/
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.