Как мне выполнить xargs grep для вывода grep с пробелами?

8

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

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp" | grep "<name regex>" | xargs grep "<content regex>"

Проблема, с которой я сталкиваюсь, заключается в том, что в некоторых путях есть пробелы, что приводит в замешательство xargs. Я знаю, что если бы я просто использовал find, я мог бы использовать -print0аргумент (вместе с -0аргументом on xargs), чтобы Xargs не рассматривал пробелы как разделители. Есть ли что-то подобное с grep?

Или я совсем неправильно подхожу к этой проблеме? Наивно, findчтобы grepв xargs grepимеет смысл для меня, но я открыт для других подходов , которые дают те же результаты.

quanticle
источник
2
Вы можете позиционировать аргументы с xargsпомощью -iпараметра, а ля cat sample.txt | grep "pat t ern" | xargs -i grep "{}"фигурные скобки сообщают ему, где позиционировать аргумент. Руководство говорит мне, что -iэто устарело в пользу, -Iтак что, возможно, стоит взглянуть на это тоже.
dougBTV

Ответы:

5

Возможно, используйте что-то вроде этого (если GNU grep).

grep -r 'content pattern' --include==*.cpp

человек grep

--include = GLOB Поиск только файлов, базовое имя которых соответствует GLOB (с использованием подстановочных знаков, как описано в --exclude)

Также см. Параметры для нулевых разделителей.

-Z, --null Вывести нулевой байт (символ ASCII NUL) вместо символа, который обычно следует за именем файла. Например, grep -lZ выводит нулевой байт после каждого имени файла вместо обычной новой строки. Эта опция делает вывод однозначным, даже при наличии имен файлов, содержащих необычные символы, такие как переводы строк. Эта опция может использоваться с такими командами, как find -print0, perl -0, sort -z и xargs -0 для обработки произвольных имен файлов, даже тех, которые содержат символы новой строки.

-z, --null-data Обрабатывать ввод как набор строк, каждая из которых заканчивается нулевым байтом (символ ASCII NUL) вместо новой строки. Как и параметр -Z или --null, этот параметр можно использовать с такими командами, как sort -z, для обработки произвольных имен файлов.

Zoredache
источник
Обратите внимание, что grep -r include='*.cpp'это глобус оболочки - и так же выровнен по функциям без / find . -name '*.cpp' -exec grep -e 'content_pattern' -- {} \;сfind . -name '*.cpp' | grep 'name_pattern' | xargs grep 'content_pattern'
mikeserv
4

Если вам приходится прыгать через много обручей, то эффективность xargs все равно теряется. Вот одна грубая работа:

find . -iname "*.cpp" | grep "<pattern>" | while read -r x; do grep exa "$x"; done

Каждый раз, когда я сталкиваюсь с проблемами с пробелами в именах файлов, ответом является двойная кавычка для переменной.

Baazigar
источник
Это запускает внутренний grep цикла уникально для каждой строки, найденной внешним grep. Это много накладных расходов.
Адам Кац
3

Используйте, findчтобы сделать всю фильтрацию имени файла. Скорее, чем

find . -name "*.cpp" | grep "foo" | xargs grep 

делать

find . -name "*.cpp" -name "*foo*" -print0 | xargs -0 grep 

Если вы хотите сделать что-то немного более сложное, например,

find . -name "*.cpp" | egrep "foo|bar" | xargs grep 

ты можешь сделать

find . -name "*.cpp" "(" -name "*foo*" -o -name "*bar*" ")" -print0 | xargs -0 grep 

Обратите внимание, что они должны работать даже для файлов с символами новой строки в их именах.

И, если вам нужна мощь полноценных регулярных выражений, вы можете использовать -regex.

Скотт
источник
2

Это должно работать даже без инструментов GNU:

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp"  | grep "<name regex>" | perl -pe 's/\n/\0/' \
  | xargs -0 grep "<content regex>"

perlВызов заменяет разрывы строк с символами NULL, что позволит xargs -0интерпретировать ввод на каждой линии основе , а не на основе каждого пробельных.

Использование GNU, вы можете удалить perlвызов и изменения xargs -0 …вxargs -d "\n" …

Нет perlили GNU? Попробуй awk '{printf "%s%c", $0, 0}'вместо этого.

Адам Кац
источник
1
Это может не сработать, если некоторые имена файлов содержат символы новой строки (довольно необычное, конечно, но не невозможное).
дхаг
У @dhag есть верное замечание относительно xargs -d "\n". Это очень необычное явление, но если у вас нет контроля над данными и вы беспокоитесь о том, что это может быть угрозой безопасности, будьте осторожны с ожидаемыми результатами.
Адам Кац