Как использовать> в команде xargs?

160

Я хочу найти команду bash, которая позволит мне grep каждый файл в каталоге и записать вывод этого grep в отдельный файл. Мое предположение было бы сделать что-то вроде этого

ls -1 | xargs -I{} "grep ABC '{}' > '{}'.out"

но насколько я знаю, xargs не нравятся двойные кавычки. Однако, если я удаляю двойные кавычки, команда перенаправляет вывод всей команды в один файл с именем «{}». Вместо ряда отдельных файлов.

Кто-нибудь знает способ сделать это с помощью xargs? Я просто использовал этот сценарий grep в качестве примера, чтобы проиллюстрировать мою проблему с xargs, поэтому любые решения, которые не используют xargs, не подходят для меня.

Джесси Шие
источник

Ответы:

201

Не делайте ошибку, делая это:

sh -c "grep ABC {} > {}.out"

Это сломается при многих условиях, в том числе прикольных именах файлов, и невозможно правильно процитировать. Вы {}всегда должны быть отдельным аргументом команды, чтобы избежать ошибок при внедрении кода. Что вам нужно сделать, это:

xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}

Относится xargsкак и к find.

Между прочим, никогда не используйте xargs без -0опции (если только для очень редкого и контролируемого однократного интерактивного использования, когда вы не беспокоитесь об уничтожении ваших данных).

И не разбирайся ls. Когда-либо. Используйте globbing или findвместо:http://mywiki.wooledge.org/ParsingLs

Используйте findдля всего, что требует рекурсии, и простой цикл с глобусом для всего остального:

find /foo -exec sh -c 'grep "$1" > "$1.out"' -- {} \;

или нерекурсивный:

for file in *; do grep "$file" > "$file.out"; done

Обратите внимание на правильное использование цитат.

lhunath
источник
Проголосовал, но сомнение regd. без использования xargsбез -0: это применимо только тогда, когда вы выводите данные findс помощью канала xargs, верно? когда я делаю, xargs -a <input_file>как бы я использовал это? Большинство команд, например, с grepвыходами \nи без них \0.. Единственный способ обойти это, я думаю, использовать trснова, чтобы исправить это. Но почему важно использовать его только с -0?
legends2k
3
@ legends2k, потому что когда вы не используете -0, xargsвозьмет ваши имена файлов и разбит все пробелы, кавычки и обратные слеши в них. Вы должны просто забыть о xargsкачестве инструмента. Если у вас есть строки, используйте цикл bash для итерации строк: while read line; do <command> "$REPLY"; done < file-with-linesилиcommand | while ...
lhunath
1
Вау, не знал об этом, спасибо за детали! Поэтому для переносимости (поскольку не все xargsявляются GNU), xargsследует избегать, если с ним нельзя использовать -0. Спасибо.
legends2k
1
Хотя я ценю подробное объяснение этого конкретного случая использования, вопрос заключается в перенаправлении вывода xargs, который не всегда включает в себя анализ lsили использование sh -c. Это не дает ни малейшего ответа на вопрос, но является первым результатом Google для вопроса, только добавляя путаницу.
pandasauce
1
@Ihunath, Привет, твой ответ мне подходит. Но не могли бы вы дать подробное объяснение или ссылки xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}? В частности, правила встроенных (двойных) кавычек и символ «-» в конце. Спасибо
Скотт Ян
40

Решение без xargsзаключается в следующем:

find . -mindepth 1 -maxdepth 1 -type f -exec sh -c "grep ABC '{}' > '{}.out'" \;

... и то же самое можно сделать с xargs , получается:

ls -1 | xargs -I {} sh -c "grep ABC '{}' > '{}.out'"

Изменить : одиночные кавычки добавлены после замечания от lhunath .

Stephan202
источник
Он сказал, что хочет использовать xargs. Я также опубликовал решение без него, но удалил, как только увидел, что ему нужны xargs.
Zifre
Ты прав. Причина, по которой я опубликовал свой ответ, заключалась в том, что лучше иметь альтернативное решение для выполнения работы, чем вообще ничего. Оказывается, это поставило меня на правильный путь, чтобы найти нужный ответ (т. Е. Трюк sh -c).
Stephan202
14

Я предполагаю, что ваш пример - это просто пример, который вам может понадобиться> для других целей. GNU Parallel http://www.gnu.org/software/parallel/ может помочь вам. Он не нуждается в дополнительном цитировании, если ваши имена файлов не содержат \ n:

ls | parallel "grep ABC {} > {}.out"

Если у вас есть имена файлов с \ n:

find . -print0 | parallel -0 "grep ABC {} > {}.out"

В качестве дополнительного бонуса вы получаете задания, выполняемые параллельно.

Посмотрите вступительные видео, чтобы узнать больше: http://pi.dk/1

10-секундная установка попытается выполнить полную установку; в случае неудачи - личная установка; если это не удается, минимальная установка:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

Если вам нужно переместить его на сервер, на котором не установлен GNU Parallel, попробуйте parallel --embed.

Оле Танге
источник