Помилка "Аргумент занадто довгий" під час копіювання великої кількості файлів


12

Я використовую таку команду:

\cp -uf /home/ftpuser1/public_html/ftparea/*.jpg /home/ftpuser2/public_html/ftparea/

І я отримую помилку:

-bash: /bin/cp: Argument list too long

Я також спробував:

ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} /home/ftpuser2/public_html/ftparea/

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

ЯКІ-небудь ідеї?


Я намагаюся скопіювати всі jpgs з 1 каталогу в інший, але лише нові файли та ті, які були оновлені.
icelizard

lsне покликаний робити подібні речі. Використовуйте find.
Призупинено до подальшого повідомлення.

Проблема не з ls, а з кількістю аргументів оболонка передається ls. Ви отримаєте ту саму помилку з vi або з будь-якою невбудованою командою.
chris

Але lsце особливо не призначені для цього: mywiki.wooledge.org/ParsingLs
НЕ Припинено до подальшого повідомлення.

Правда, але в цьому випадку помилка не через помилку розбору з ls, це передачу мільярду аргументів новому процесу, який, як буває, ls. На додаток до неналежного використання ls, трапляється наштовхуватися на обмеження ресурсів / дизайну unix. У цьому випадку у пацієнта болить живіт і зламана нога.
chris

Відповіді:


19

* .jpg розширюється до списку довше, ніж оболонка може обробляти. Спробуйте це замість цього

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;

Я використав find / home / ftpuser1 / public_html / ftparea / -name "* jpg" -exec cp -uf "{}" / home / ftpuser2 / public_html / ftparea / і отримав таку помилку пошуку: відсутній аргумент до `-exec '
icelizard

Вам не вистачає останнього аргументу cp, відповідач сказав вам правильно. Перевірте свою реалізацію. Зауважте, що у цій відповіді крапка в "* .jpg" відсутня, це може призвести до недобросовісного поведінки (наприклад, наприклад, dir, названий "myjpg"). Зауважте, що це може бути паранояльно, але безпечніше чітко вказати, що ви збираєтесь скопіювати, використовуючи файл -type (запобігаючи
впливам брудів

Після ретельного огляду я пропустив "\;" закінчити команду, яку повинен виконати -exec. Дурний мене!
icelizard

@AlberT: спасибі за голову за відсутню крапку. Це був друкарський помилок. Відповідь оновлено.
Шон Чін

Справа не в тому, що cp не впорається з цим. Оболонка не може.
d -_- b

6

Існує максимальний ліміт, скільки може тривати список аргументів для системних команд - цей ліміт залежить від дистрибутива на основі значення, MAX_ARG_PAGESколи ядро ​​компілюється, і його неможливо змінити без перекомпіляції ядра.

Зважаючи на те, як обробка керується оболонкою оболонкою, це вплине на більшість системних команд, коли ви використовуєте той самий аргумент ("* .jpg"). Оскільки глобул обробляється спочатку оболонкою, а потім надсилається команді, команда:

cp -uf *.jpg /targetdir/

по суті те ж саме з оболонкою, як якщо б ви написали:

cp -uf 1.jpg 2.jpg ... n-1.jpg n.jpg /targetdir/

Якщо ви маєте справу з великою кількістю jpegs, це може стати некерованим дуже швидко. Залежно від вашого правила іменування та кількості файлів, які ви насправді повинні обробити, ви можете запустити команду cp одночасно в іншому підмножині каталогу:

cp -uf /sourcedir/[a-m]*.jpg /targetdir/
cp -uf /sourcedir/[n-z]*.jpg /targetdir/

Це може спрацювати, але те, наскільки ефективно це було б, базується на тому, наскільки добре ви можете розбити список файлів на зручні глобальні блоки.

Глобальний. Мені подобається це слово.

Деякі команди, такі як find та xargs , можуть обробляти великі списки файлів, не створюючи болісно розміщених списків аргументів.

find /sourcedir/ -name '*.jpg' -exec cp -uf {} /targetdir/ \;

Аргумент -exec буде запускати решту командного рядка один раз для кожного знайденого файлу пошуку , замінюючи {} кожним знайденим іменем файлу. Оскільки команда cp одночасно виконується лише в одному файлі, обмеження списку аргументів не є проблемою.

Це може бути повільним через необхідність обробляти кожен файл окремо. Використання xargs може забезпечити більш ефективне рішення:

find /sourcedir/ -name '*.jpg' -print0 | xargs -0 cp -uf -t /destdir/

xargs може взяти повний список файлів, наданий знахідкою , і розбити його на списки аргументів керованих розмірів та запустити cp у кожному з цих підсписок.

Звичайно, є також можливість просто перекомпілювати ваше ядро, встановити більшу величину для MAX_ARG_PAGES. Але перекомпіляція ядра - це більша робота, ніж я готовий пояснити у цій відповіді.


Я поняття не маю, чому це було знято. Це єдина відповідь, яка, здається, пояснює, чому це відбувається. Може тому, що ви не запропонували використовувати xargs як оптимізацію?
chris

Додано до рішення xargs, але я все ще переживаю, що події є через те, що в моїх деталях явно не так, і ніхто не хоче мені говорити, що це таке. :(
золотоPseudo

xargsвидається набагато ефективнішим, оскільки в результаті кількість викликів команд набагато менша. У моєму випадку я бачу в 6-12 разів кращу продуктивність при використанні, argsтоді як при використанні -execрішення зі зростанням кількості файлів зростає ефективність.
Ян Вльчинський

3

Це трапляється тому, що ваш підстановочний вираз ( *.jpg) перевищує обмеження довжини аргументу командного рядка при розширенні (можливо, тому, що у вас є багато файлів .jpg /home/ftpuser/public_html/ftparea).

Існує кілька способів обійти таке обмеження, як, наприклад, використання findабо xargs. Перегляньте цю статтю для отримання більш детальної інформації про те, як це зробити.


+1 за хороший зовнішній ресурс з теми.
viam0Zah

3

Як коментує GoldPseudo, існує обмеження кількості аргументів, які ви можете передати процесу, який ви нерестуєте. Дивіться його відповідь для гарного опису цього параметра.

Ви можете уникнути проблеми, не передавши процесу занадто багато аргументів, або зменшивши кількість аргументів, які ви передаєте.

Цикл для оболонки в оболонці, знайдіть, і ls, grep, і цикл деякий час роблять те ж саме в цій ситуації -

for file in /path/to/directory/*.jpg ; 
do
  rm "$file"
done

і

find /path/to/directory/ -name '*.jpg' -exec rm  {} \;

і

ls /path/to/directory/ | 
  grep "\.jpg$" | 
  while
    read file
  do
    rm "$file"
  done

всі мають одну програму, яка зчитує каталог (сама оболонка, find і ls) та іншу програму, яка фактично приймає один аргумент за виконання та повторює весь список команд.

Тепер це буде повільно, тому що rm потрібно розщедрити та виконати для кожного файлу, що відповідає шаблону * .jpg.

Тут грає xargs. xargs приймає стандартне введення, і для кожного N (для freebsd це 5000 за замовчуванням) він породжує одну програму з N аргументів. xargs - це оптимізація вищезазначених циклів, тому що вам потрібно лише розщедрити програми 1 / N для ітерації над усім набором файлів, які читають аргументи з командного рядка.


2

Існує максимальна кількість аргументів, які можна вказати програмі, bash розширює * .jpg на безліч аргументів cp. Ви можете вирішити це за допомогою find, xargs або rsync тощо.

Погляньте тут про xargs та знайдіть

/programming/143171/how-can-i-use-xargs-to-copy-files-that-have-spaces-and-quotes-in-their-names


1

Глобус '*' розширюється до занадто багато імен. Використовуйте замість find / home / ftpuser / public_html -name '* .jpg'.


Знайти та відлуння * приводить до одного результату - ключовим тут є використання xargs, а не просто передача всіх 1 мільярдів аргументів командного рядка до команди, яку оболонка намагається розщедрити.
chris

echo * не вдасться, якщо файлів занадто багато, але знайти вдасться. Також використання find -exec з + еквівалентно використанню xargs. (Однак не всі знаходять підтримку +)
Вільям Перселл

1

Використання +опції для find -execзначно пришвидшить операцію.

find  /home/ftpuser/public_html/ftparea/ -name "*jpg" -exec cp -uf -t /your/destination "{}" +

+Варіант вимагає , {}щоб бути останнім аргументом тому використання -t /your/destination(або --target-directory=/your/destination) опції cpробить його роботу.

Від man find:

команда -exec {} +

          This  variant  of the -exec action runs the specified command on  
          the selected files, but the command line is built  by  appending  
          each  selected file name at the end; the total number of invoca  
          tions of the command will  be  much  less  than  the  number  of  
          matched  files.   The command line is built in much the same way  
          that xargs builds its command lines.  Only one instance of  ‘{}’  
          is  allowed  within the command.  The command is executed in the  
          starting directory.

Правка : переставлені аргументи на cp


Я шукаю: відсутні аргументи до `-exec '/ home / ftpuser1 / public_html / ftparea / -name' * jpg '-exec cp -uf" {} "/ home / ftpuser2 / public_html / ftparea / +
icelizard

Я переставив аргументи, cpщоб виправити цю помилку.
Призупинено до подальшого повідомлення.

1

Це здається, що у вас *.jpgв каталозі занадто багато файлів, щоб одразу помістити їх у командний рядок. Ви можете спробувати:

find /home/ftpuser/public_html/ftparea1 -name '*.jpg' | xargs -I {} cp -uf {} /home/ftpuser/public_html/ftparea2/

Можливо, вам доведеться перевірити man xargsсвою реалізацію, щоб перевірити, чи -Iправильно встановлений комутатор для вашої системи.

Насправді ви справді маєте намір скопіювати ці файли в те саме місце, де вони вже є?


вибачте, це два різних каталоги: ftpuser1 та ftpuser2
icelizard

Щойно спробував це: ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea / Все ще отримав -bash: / bin / ls: Аргумент занадто довгий
icelizard

Ох, ви цілком праві, звичайно, lsбудете мати таку ж проблему! Я змінив те, findщо не буде.
Грег Хьюгілл

0

Перейдіть до папки

cd /home/ftpuser1/public_html/

і виконати наступне:

cp -R ftparea/ /home/ftpuser2/public_html/

Таким чином, якщо папка 'ftparea' має підпапки, це може бути негативним ефектом, якщо ви хочете, щоб з неї були лише файли '* .jpg', але якщо немає вкладених папок, такий підхід, безумовно, буде набагато швидшим, ніж використовуючи пошук і xargs

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.