Я пытаюсь скопировать кучу файлов под каталогом, и некоторые файлы содержат пробелы и одинарные кавычки в своих именах. Когда я пытаюсь связать find
и grep
с xargs
, я получаю следующую ошибку:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
Есть предложения по более надежному использованию xargs?
Это на Mac OS X 10.5.3 (Leopard) с BSD xargs
.
Сообщение об ошибке GNU xargs для этого с именем файла, содержащим одинарную кавычку, гораздо более полезно: xargs: непревзойденная одинарная кавычка; по умолчанию кавычки являются специальными для xargs, если вы не используете параметр -0. — person Drew Stephens schedule 27.09.2008
GNU xargs также имеет параметр --delimiter
(-d
). Попробуйте использовать \n
в качестве разделителя. Это не позволяет xargs
разделять строки пробелами на несколько слов / аргументов. — person Drew Stephens schedule 27.02.2017
Вы можете объединить все это в одну команду
find
:Это будет обрабатывать имена файлов и каталоги с пробелами в них. Вы можете использовать
-name
для получения результатов с учетом регистра.Примечание. Флаг
--
, переданныйcp
, предотвращает обработку файлов, начинающихся с-
в качестве параметров.-exec будет работать с любой находкой, я никогда не понимал, почему люди используют xargs (и просто подождите, пока вы не достигнете предела длины каталога xargs !!) — person Drew Stephens; 02.10.2008
Что такое «ограничение длины каталога xargs»? Вы имеете в виду максимальный размер команды? Если да, предполагается, что xargs разбивает свои аргументы на группы подходящего размера. — person Drew Stephens; 14.10.2008
Люди используют xargs, потому что обычно быстрее вызывать исполняемый файл 5 раз с 200 аргументами каждый раз, чем каждый раз вызывать его 1000 раз с одним аргументом. — person Drew Stephens; 14.10.2008
Ответ Криса Джестера-Янга должен быть здесь хорошим ответом … Кстати, это решение не работает, если имя файла начинается с -. По крайней мере, это необходимо — после cp. — person Drew Stephens; 24.01.2009
Пример скорости — более 829 файлов, метод find -exec занял 26 секунд, в то время как find -print0 | xargs —null method tool 0,7 секунды. Значительная разница. — person Drew Stephens; 27.08.2012
Использование cp — это нормально, но не так — в этом случае cp запускается для каждого файла, что накладывает нетривиальные накладные расходы на fork + execve для каждого отдельного файла. Лучше использовать параметр -t команды cp. Нравится:
find .. -exec cp -t ~/foo/bar -- {} +
— person Drew Stephens; 30.01.2013@tzot Поздний комментарий, но в любом случае
xargs
не требуется для решения проблемы, которую вы описываете,find
уже поддерживает его с помощью-exec
+
знаков препинания. — person Drew Stephens; 26.06.2013Привет, если
foobar
— результат какой-то другой команды, как я могу это сделать? Большое тебе спасибо — person Drew Stephens; 21.05.2015@ ying17zi Замените
foobar
на$(your-command args)
или`your-command args`
. — person Drew Stephens; 24.05.2015не отвечает на вопрос, как работать с пробелами — person Drew Stephens; 11.01.2019
@BenGlasser Помещение
{}
в кавычки защищает пробелы в имени файла / пути. Это то, о чем вы говорите? — person Drew Stephens; 12.01.2019@godbyk no Я имею в виду, что этот ответ до сих пор не ответил на вопрос о том, как обрабатывать пробелы с помощью xargs, который является заголовком вопроса — person Drew Stephens; 14.01.2019
@BenGlasser А, попался. См. ответ Криса об использовании
xargs
с пробелами. — person Drew Stephens; 14.01.2019find . -print0 | grep --null 'FooBar' | xargs -0 ...
Я не знаю, поддерживает ли
grep
--null
и поддерживает лиxargs
-0
на Leopard, но на GNU все хорошо.Leopard поддерживает -Z (это GNU grep) и, конечно же, find (1) и xargs (1) поддерживают -0. — person Drew Stephens; 24.01.2009
@Keltia Обратите внимание, что в OS X Mountain Lion (10.8) Apple заменила GNU grep на BSD grep, и переключатель — {z | Z} не работает. — person Drew Stephens; 28.11.2012
В OS X 10.9
grep -{z|Z}
означает вести себя как zgrep (распаковывать), а не предполагать печать нулевого байта после каждого имени файла. Используйтеgrep --null
для достижения последнего. — person Drew Stephens; 07.02.2014@ ChrisJester-Young Добро пожаловать. Хотя теперь ваш комментарий по поводу опции -z потерял свой контекст;) — person Drew Stephens; 07.02.2014
Что не так с
find . -name 'FooBar' -print0 | xargs -0 ...
? — person Drew Stephens; 17.05.2014@QuentinPradet Очевидно, что для фиксированной строки, такой как FooBar,
-name
или-path
работают нормально. OP указали использованиеgrep
, предположительно потому, что они хотят фильтровать список с помощью регулярных выражений. — person Drew Stephens; 17.05.2014Вы можете получить
Binary file (standard input) matches
от grep, когда попробуете это из-за-print0
; используйтеgrep -a
, чтобы заставить его интерпретировать канал. — person Drew Stephens; 21.06.2015Не работает с
duperemove
— передача имени файла с нулевым окончанием приводит кNo such file or directory
. — person Drew Stephens; 30.07.2018Хорошо, я подумал, вы неправильно используете xargs. Вы должны использовать
xargs -d '\n'
, иначе xargs разбивает файлы на пробелы. Попробуйте следующее:touch "foo bar"; echo -e "foo bar" | xargs -0 ls
, вы получите сообщение об ошибке. Но замена-0
на-d '\n'
работает. См. также этот ответ. — person Drew Stephens; 30.07.2018@ Hi-Angel. Именно поэтому именно я использую
xargs -0
вместе сfind -print0
. Последний печатает имена файлов с ограничителем NUL, а первый получает файлы таким образом. Почему? Имена файлов в Unix могут содержать символы новой строки. Но они не могут содержать символы NUL. — person Drew Stephens; 30.07.2018Используйте это, если вам нужны другие аргументы, например целевой каталог для cp:
... | xargs -0 -J % cp -v % /target/dir
— person Drew Stephens; 31.12.2020Изучите возможность использования параметра командной строки —null для xargs с параметром -print0 в find.
Это более эффективно, поскольку он не запускает «cp» несколько раз:
У меня это не сработало. Он пытался вставить ~ / foo / bar во все, что вы нашли, но не наоборот — person Drew Stephens; 21.06.2010
Флаг -t для cp является расширением GNU, AFAIK, и недоступен в OS X. Но если бы это было так, оно бы работало, как показано в этом ответе. — person Drew Stephens; 18.05.2012
Я использую Linux. Спасибо за ключ -t. Вот чего мне не хватало — person Drew Stephens; 21.04.2017
Имейте в виду, что большинство параметров, обсуждаемых в других ответах, не являются стандартными для платформ, которые не используют утилиты GNU (например, Solaris, AIX, HP-UX). См. «Стандартное» поведение xargs в спецификации POSIX.
Я также считаю, что поведение xargs, при котором он запускает команду хотя бы один раз, даже без ввода, является неприятным.
Я написал свою собственную частную версию xargs (xargl) для решения проблем с пробелами в именах (разделяются только новые строки, хотя комбинация find … -print0 и xargs -0 довольно удобна, учитывая, что имена файлов не могут содержат символы ASCII NUL ‘\ 0’. Мой xargl не так полон, как хотелось бы, чтобы его можно было опубликовать — тем более, что в GNU есть средства, которые не менее хороши.
GitHub или этого не произошло — person Drew Stephens; 26.09.2017
@CoreyGoldberg: Думаю, тогда этого не произошло. — person Drew Stephens; 26.09.2017
POSIX
find
вообще не нуждается вxargs
(и это уже было верно 11 лет назад). — person Drew Stephens; 11.02.2019Я обнаружил, что мне подходит следующий синтаксис.
В этом примере я ищу 200 самых больших файлов размером более 1 000 000 байт в файловой системе, смонтированной в «/ usr / pcapps».
Строка Perl между «find» и «xargs» экранирует / заключает в кавычки каждый пробел, поэтому «xargs» передает любое имя файла со встроенными пробелами в «ls» в качестве единственного аргумента.
Perl-версия bill_starr не будет работать со встроенными символами новой строки (справляется только с пробелами). Для тех, кто, например, Solaris, где у вас нет инструментов GNU, может быть более полная версия (с использованием sed) …
отрегулируйте аргументы find и grep или другие команды по своему усмотрению, но sed исправит ваши встроенные символы новой строки / пробелы / табуляции.
Я использовал Ответ Билла Стар немного изменен в Solaris:
Это поместит кавычки вокруг каждой строки. Я не использовал параметр ‘-l’, хотя он, вероятно, поможет.
Список файлов, который я собирался, мог иметь ‘-‘, но не символы новой строки. Я не использовал выходной файл с другими командами, так как хочу просмотреть, что было найдено, прежде чем я начну массово удалять их с помощью xargs.
Я считаю, что это будет надежно работать для любого символа, кроме перевода строки (и я подозреваю, что если у вас есть переводы строки в ваших именах файлов, то у вас есть более серьезные проблемы, чем эта). Для этого не требуется GNU findutils, только Perl, поэтому он должен работать практически везде.
Можно ли использовать перевод строки в имени файла? Никогда об этом не слышал. — person Drew Stephens; 16.05.2012
В самом деле. Попробуйте, например,
mkdir test && cd test && perl -e 'open $fh, ">", "this-file-contains-a-\n-here"' && ls | od -tx1
— person Drew Stephens; 17.05.2012|perl -lne 'print quotemeta'
это именно то, что я искал. Другие сообщения здесь мне не помогли, потому что вместоfind
мне нужно было использоватьgrep -rl
, чтобы значительно сократить количество файлов PHP до только файлов, зараженных вредоносным ПО. — person Drew Stephens; 14.06.2013perl и quotemeta гораздо более общие, чем print0 / -0 — спасибо за общее решение конвейерной обработки файлов с пробелами — person Drew Stephens; 16.10.2015
Я столкнулся с той же проблемой. Вот как я это решил:
Я использовал
sed
, чтобы заменить каждую строку ввода той же строкой, но в двойных кавычках. Наsed
странице руководства: «… Амперсанд (` `& »), появляющийся в замене, заменяется строкой, соответствующей RE …» — в данном случае.*
, вся линия.Это решает ошибку
xargs: unterminated quote
.Я работаю в Windows и использую gnuwin32, поэтому мне пришлось использовать
sed s/.*/\"&\"/
, чтобы заставить его работать. — person Drew Stephens; 16.10.2012Да, но, по-видимому, это не будет обрабатывать имена файлов с
"
в — если sed также не цитирует кавычки? — person Drew Stephens; 27.05.2015Использование
sed
— это гениально, и на данный момент это правильное решение без переписывания проблемы! — person Drew Stephens; 03.06.2015Только не используйте
xargs
. Это отличная программа, но она не подходитfind
, когда сталкивается с нетривиальными случаями.Вот портативное (POSIX) решение, то есть такое, которое не требует
find
,xargs
илиcp
расширений GNU:Обратите внимание на окончание
+
вместо более обычного;
.Это решение:
правильно обрабатывает файлы и каталоги со встроенными пробелами, символами новой строки или любыми экзотическими символами.
работает в любой системе Unix и Linux, даже в тех, которые не предоставляют инструментарий GNU.
не использует
xargs
, красивую и полезную программу, но требует слишком больших настроек и нестандартных функций для правильной обработкиfind
вывода.также более эффективен (читайте быстрее), чем принятый и большинство, если не все другие ответы.
Также обратите внимание, что, несмотря на то, что указано в некоторых других ответах или комментариях, цитирование
{}
бесполезно (если вы не используете экзотическуюfish
shell).Почему быстрее? Пользователь tzot написал: Люди используют xargs, потому что обычно быстрее вызывать исполняемый файл 5 раз с 200 аргументами каждый раз, чем каждый раз вызывать его 1000 раз с одним аргументом. — person Drew Stephens; 29.07.2018
@PeterMortensen Вы, наверное, не замечаете финальный плюс.
find
может делать то, что делаетxargs
, без каких-либо накладных расходов. — person Drew Stephens; 30.07.2018Возможно, вам понадобится grep для каталога Foobar, например:
Согласно странице руководства
-i
устарел, и вместо него следует использовать-I
. — person Drew Stephens; 25.08.2014Этот метод работает в Mac OS X v10.7.5 (Lion):
Я также проверил точный синтаксис, который вы опубликовали. Это также нормально работало на 10.7.5.
Это работает, но
-I
подразумевает-L 1
(так говорится в руководстве), что означает, что команда cp запускается один раз для каждого файла = v медленно. — person Drew Stephens; 27.05.2015xargs -J% cp% ‹каталог назначения› Возможно, более эффективен в OSX. — person Drew Stephens; 26.01.2016
Извините, но это НЕПРАВИЛЬНО. Сначала он выдает именно ту ошибку, которой хотел избежать TO. Вы должны использовать
find ... -print0
иxargs -0
для работы вокруг xargs. По умолчанию кавычки являются особенными. Во-вторых, обычно используйте'{}'
, а не{}
в командах, передаваемых в xargs, для защиты от пробелов и специальных символов. — person Drew Stephens; 20.03.2016Извините, Андреас Шпиндлер, я не так хорошо знаком с xargs и нашел эту строку после некоторых экспериментов. Кажется, это работает для большинства людей, которые прокомментировали это и проголосовали за него. Не могли бы вы подробнее рассказать о том, какую ошибку он выдает? Кроме того, не могли бы вы опубликовать точные данные, которые, по вашему мнению, были бы более правильными? Спасибо. — person Drew Stephens; 21.03.2016
Это часть xargs, которая работала для меня в MacOS 10.15:
xargs -0 -J % cp -v % /foo/bar
— person Drew Stephens; 31.12.2020С Bash (не POSIX) вы можете использовать подстановку процесса, чтобы получить текущую строку внутри переменной. Это позволяет использовать кавычки для экранирования специальных символов:
Для тех, кто полагается на команды, отличные от find, например
ls
:Работает, но медленно, потому что
-I
подразумевает-L 1
— person Drew Stephens; 27.05.2015Если вы используете Bash, вы можете преобразовать stdout в массив строк с помощью
mapfile
:Преимущества:
Вы можете добавлять к именам файлов другие аргументы. Для
cp
вы также можете:однако у некоторых команд нет такой функции.
Недостатки:
Ну … кто знает, доступен ли Bash в OS X?
Если версии find и xarg в вашей системе не поддерживают переключатели
-print0
и-0
(например, AIX find и xargs), вы можете использовать этот ужасно выглядящий код:Здесь sed позаботится об экранировании пробелов и кавычек для xargs.
Проверено на AIX 5.3
Я немного поигрался с этим, начал обдумывать возможность модификации xargs и понял, что для того типа использования, о котором мы говорим здесь, простая повторная реализация на Python — лучшая идея.
Во-первых, наличие ~ 80 строк кода для всего этого означает, что легко понять, что происходит, а если требуется другое поведение, вы можете просто взломать его в новый скрипт за меньшее время, чем требуется, чтобы получить ответ где-то вроде переполнения стека.
См. https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs и https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py.
С написанным yargs (и установленным Python 3) вы можете ввести:
делать копирование 203 файлов за раз. (Здесь 203, конечно, просто заполнитель, и использование такого странного числа, как 203, дает понять, что это число не имеет другого значения.)
Если вам действительно нужно что-то более быстрое и без использования Python, возьмите zargs и yargs в качестве прототипов и перепишите на C ++ или C.
Что касается меня, я пытался сделать что-то немного другое. Я хотел скопировать свои файлы .txt в папку tmp. Имена файлов .txt содержат пробелы и символы апострофа. Это сработало на моем Mac.
Самый простой способ сделать то, что хочет исходный плакат, — изменить разделитель с любого пробела на символ конца строки следующим образом:
Этот ответ прост, эффективен и прямо по делу: разделитель по умолчанию, установленный для xargs, слишком широк, и его необходимо сузить для того, что хочет сделать OP. Я знаю это из первых рук, потому что сегодня я столкнулся с той же проблемой, делая что-то подобное, за исключением cygwin. Если бы я прочитал справку по команде xargs, я бы, возможно, избежал некоторых головных болей, но ваше решение исправило это за меня. Спасибо ! (Да, OP был на MacOS с использованием BSD xargs, который я не использую, но я надеюсь, что параметр xargs -d существует во всех версиях). — person Drew Stephens; 07.05.2016
Хороший ответ, но не работает на Mac. Вместо этого мы можем передать find в
sed -e 's_\(.*\)_"\1"_g'
, чтобы заключить имя файла в кавычки. — person Drew Stephens; 21.08.2016Это должен быть принятый ответ. Вопрос был об использовании
xargs
. — person Drew Stephens; 06.11.2016Я получаю
xargs: illegal option -- d
— person Drew Stephens; 12.11.2017Стоит отметить, что имена файлов могут содержать символ новой строки во многих системах * nix. Вы вряд ли когда-нибудь столкнетесь с этим в дикой природе, но если вы запускаете команды оболочки на ненадежном вводе, это может быть проблемой. — person Drew Stephens; 19.01.2019
(Пример: файлы
B
иC
существуют в большом каталоге. Пользователь создает новый файл с именемB\nC
. Позже этот пользователь уходит, и кто-то очищает все файлы этого пользователя, опрашивая владельцев и передавая список их файлов черезxargs -d "\n" rm
. ФайлыB
иC
немедленно удаляются.) — person Drew Stephens; 19.01.2019Я создал небольшой переносимый сценарий оболочки под названием «xargsL» вокруг «xargs», который решает большинство проблем.
В отличие от xargs, xargsL принимает по одному пути на строку. Имена путей могут содержать любой символ, кроме (очевидно) новой строки или байтов NUL.
В списке файлов не допускается и не поддерживается цитирование — ваши имена файлов могут содержать всевозможные пробелы, обратные косые черты, обратные кавычки, подстановочные знаки оболочки и т.п. — xargsL будет обрабатывать их как буквальные символы, никакого вреда.
В качестве дополнительной бонусной функции xargsL не запускает команду один раз, если нет ввода!
Обратите внимание на разницу:
Любые аргументы, переданные в xargsL, будут переданы в xargs.
Вот сценарий оболочки POSIX «xargsL»:
Поместите скрипт в какой-нибудь каталог в вашем $ PATH и не забудьте
$ chmod +x xargsL
сценарий, чтобы сделать его исполняемым.
Задача кадра — вы спрашиваете, как использовать xargs. Ответ: вы не используете xargs, потому что он вам не нужен.
комментарий от
user80168
описывает способ сделать это напрямую с помощью cp, без вызова cp для каждого файла:Это работает, потому что:
cp -t
позволяет указать целевой каталог ближе к началуcp
, чем ближе к концу. Отman cp
:Флаг
--
указываетcp
интерпретировать все, что идет после, как имя файла, а не флаг, поэтому файлы, начинающиеся с-
или--
, не сбивают с толкуcp
; вам все еще нужно это, потому что символы _12 _ / _ 13_ интерпретируютсяcp
, тогда как любые другие специальные символы интерпретируются оболочкой.Вариант
find -exec command {} +
по сути делает то же самое, что и xargs. Сman find
:Используя это напрямую в find, это позволяет избежать вызова канала или оболочки, так что вам не нужно беспокоиться о каких-либо неприятных символах в именах файлов.
Удивительная находка, я понятия не имел !!! -exec utility [аргумент …] {} + То же, что и -exec, за исключением того, что «{}» заменяется максимально возможным количеством путей для каждого вызова утилиты. Это поведение похоже на поведение xargs (1). в реализации BSD. — person Drew Stephens; 30.10.2019