find + xargs: слишком длинная строка аргумента

21

У меня есть такая строка:

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

но я получил следующую ошибку:

xargs: argument line too long

Я запутался. Разве использование не xargsдолжно точно помочь с этой проблемой?

Примечание: я знаю, что я могу технически использовать его -execв find, но я хотел бы понять, почему вышеперечисленное не работает, так как я понимаю, что 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 {} +
SLM
источник
Благодарность! Когда вы сказали, "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 {}.

При наличии только этой опции -ixargs ожидал разделенный символом новой строки список имен файлов. Поскольку во входных данных, вероятно, не было новой строки, 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 !
nelaaro
источник