find + xargs: рядок аргументу занадто довгий


21

У мене є такий рядок:

find /foo/bar -name '*.mp4' -print0 | xargs -i {} -0 mv -t /some/path {}

але я отримав таку помилку:

xargs: argument line too long

Я збентежений. Хіба використання цього не xargsповинно точно допомогти у цій проблемі?

Примітка. Я знаю, що я можу використовувати технічно -execв пошуку, але я хотів би зрозуміти, чому вищезгадане не вдається, оскільки я розумію, що xargsслід знати, як розділити вхід на керований розмір на аргумент, який він працює. Це неправда?

Це все з zsh.

Відповіді:


11

Ну для однієї речі -iперемикач застарілий:

-i[replace-str]
     This  option  is a synonym for -Ireplace-str if replace-str is specified. 
     If the replace-str argument is missing, the effect is the same as -I{}. 
     This option is deprecated; use -I instead.

Тож коли я змінив вашу команду на це, це спрацювало:

$ find /foo/bar -name '*.mp4' -print0 | xargs -I{} -0 mv -t /some/path {}

Приклад

$ find . -print0 | xargs -I{} -0 echo {}
.
./.sshmenu
./The GIT version control system.html
./.vim_SO
./.vim_SO/README.txt
./.vim_SO/.git
./.vim_SO/.git/objects
./.vim_SO/.git/objects/pack
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.idx
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.pack
./.vim_SO/.git/objects/info
./.vim_SO/.git/refs
./.vim_SO/.git/refs/tags
...

Використання -I{}

Цей підхід не слід використовувати, оскільки працює ця командна команда:

$ find -print0 ... | xargs -I{} -0 ...

неявно вмикає ці перемикачі на xargs, -xі -L 1. У -L 1конфигурирует xargsтак , що він дзвонить команди , які ви хочете запустити файли через в одній моді.

Таким чином, це перемагає мету використання xargsтут, оскільки якщо ви дасте йому 1000 файлів, команда буде виконувати mvкоманду 1000 разів.

То який підхід я повинен використовувати тоді?

Ви можете зробити це за допомогою xargs так:

$ find /foot/bar/ -name '*.mp4' -print0 | xargs -0 mv -t /some/path

Або просто знайдіть це все:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +

Спасибі! Коли ви сказали, "This approach shouldn't be used"який підхід слід використовувати замість цього? Було "find /foot/bar/ -name '*.csv' -print0 | xargs -0 mv -t some_dir'"б краще рішення? Якщо так, то як же xargsзнати в цьому випадку , коли в mvкоманді годуватися аргументів він отримує з труби? (чи завжди це їх останні?)
Амеліо Васкес-Рейна

@ User815423426 - Робити це з тільки find ... -exec ...кращим шляхом , або якщо ви хочете використовувати це теж добре. Так, це завжди ставить їх останніми. Ось чому цей метод потребує . xargsfind ... | xargs ... mv -t ...-t
slm

5

Опція -iбере необов’язковий аргумент. Оскільки ви поставили пробіл після -i, не було жодного аргументу до -iваріанту, і тому подальший -0не був варіантом, xargsа другим із 6 операндів {} -0 mv -t /some/path {}.

З єдиною опцією -i, xargs очікував, що розділений новим рядком список назв файлів. Оскільки, мабуть, не було нового рядка у введенні, xargs отримав те, що було схоже на величезне ім'я файлу (із вбудованими нульовими байтами, але xargs цього не перевірив). Цей єдиний рядок, що містить весь вихід, findбув довший максимальної довжини командного рядка, отже, помилка «командний рядок занадто довга».

Ваша команда працювала б -i{}замість цього -i {}. Крім того, ви могли б використовувати -I {}: -Iє подібним до -i, але приймає обов'язковий аргумент, тому наступний аргумент, переданий до xargs, використовується як аргумент -Iопції. Тоді аргумент після цього, -0який інтерпретується як варіант тощо.

Однак вам взагалі не слід користуватися -I {}. Використання -Iмає три ефекти:

  • -Iвимикає обробку цитат, що -0вже є.
  • -Iзмінює рядок для заміни, але {}це значення за замовчуванням.
  • -Iпризводить до того, що команда виконується окремо для кожного запису вводу, що тут марно, оскільки ваша команда ( mv -t) спеціально призначена для впорядкування декількох файлів за виклик.

Або краплі, -Iі -iвзагалі

find /foo/bar -name '*.mp4' -print0 | xargs -0 mv -t /some/path {}

або скиньте xargs та використовуйте -exec:

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +

0

Спробуйте використовувати bash для циклу:

for FILE in *.mp4 ; do rm $FILE ; done

або якщо ви хочете побачити, що відбувається:

for FILE in *.mp4 ; do echo Removing $FILE ; rm $FILE ; done

0

Якщо ви бачите це під час використання рибної оболонки .
Це стосується того, як риба розширює рядок заміни{}

Якщо ви використовуєте рибу, вам потрібно уникнути рядок заміни \{\}

| xargs -I \{\} echo \{\}

або використовувати інший рядок заміни

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