Как обработать каждую строку, полученную в результате команды grep


152

У меня есть ряд строк, извлеченных из файла после выполнения команды grep следующим образом:

var=`grep xyz abc.txt`

Допустим, в результате я получил 10 строк, состоящих из xyz.

Теперь мне нужно обработать каждую строку, полученную в результате выполнения команды grep. Как мне поступить для этого?


6
Ни один из ответов здесь не упоминает силу grep -oдля такого рода вещей. -oФлаг будет возвращать только текст , который соответствует, с одной спички на линии выхода. (Он не является исчерпывающим, поэтому echo aaa |grep 'a*'дает только «ааа» и пропускает три частичных совпадения »,« а »и« аа »)
Адам Кац

Ответы:


269

Один из простых способов - не сохранять выходные данные в переменной, а непосредственно перебирать их с помощью цикла while / read.

Что-то вроде:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

Существуют варианты этой схемы в зависимости от того, что именно вы ищете.

Если вам нужно изменить переменные внутри цикла (и сделать это изменение видимым вне его), вы можете использовать подстановку процесса, как указано в ответе fedorqui :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)

если нет строки с xyz?
XYZ_Linux

Тогда ничего не происходит, цикл не запускается.
Мат

13
Проблема этого метода заключается в том, что (из-за конвейера) все внутри цикла находится в подоболочке, поэтому установка переменных, определенных вне цикла во время цикла, не делает их значения доступными после цикла!
Дэвид Дория

3
@ Дэвид: предоставил альтернативу для решения вашей проблемы. (Федорки уже говорил об этом тоже.)
Мат

Для команд, последняя строка вывода которых не заканчивается символом новой строки, вам понадобится: while read p || [[ -n $p ]]; do ...(заимствовано из stackoverflow.com/questions/1521462/… )
Охад Шнайдер

21

Вы можете сделать следующее while read цикл, который будет подпитываться результатом grepкоманды, используя так называемую подстановку процесса:

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

Таким образом, вам не нужно сохранять результат в переменной, а непосредственно «вводить» его вывод в цикл.


Обратите внимание на использование IFS=и в read -rсоответствии с рекомендациями BashFAQ / 001: Как я могу читать файл (поток данных, переменную) построчно (и / или поле за полем)? :

Параметр -r для чтения предотвращает интерпретацию с обратной косой чертой (обычно используется как пара новой строки с обратной косой чертой, для продолжения через несколько строк или для экранирования разделителей). Без этой опции любые неэкранированные обратные слэши на входе будут отброшены. Вы почти всегда должны использовать опцию -r с read.

В приведенном выше сценарии IFS = предотвращает обрезку начальных и конечных пробелов. Удалите его, если хотите этот эффект.

Что касается подстановки процесса, это объясняется на странице bash hackers :

Подстановка процесса - это форма перенаправления, в которой ввод или вывод процесса (некоторая последовательность команд) отображается как временный файл.


ОК, я ударил forверсию. Попытался сделать цикл, "${$(grep xyz abc.txt)[@]}"как в stackoverflow.com/a/14588210/1983854, но не смог. Поэтому я просто оставляю первую версию.
Федорки 'ТАК прекрати вредить'

1
Вы не можете применить расширение параметров к подстановке команд (если вы не используете zsh, где, вероятно, работает такой тип вложенности).
chepner

Одна из возможных проблем с этой идиомой заключается в том, что если что-то внутри цикла пытается прочитать со стандартного ввода, оно получит часть файла. Чтобы избежать этой возможности, мне нравится отправлять файл через файловый дескриптор 3, а не через stdin. Просто используйте while IFS= read -r result <&3иdone 3< <(grep ...
Гордон Дэвиссон

8

Я бы предложил использовать awk вместо grep + что-то еще здесь.

awk '$0~/xyz/{ //your code goes here}' abc.txt


1
Как бы вы сослались на полную найденную строку в //your code goes here?
user857990

2
@ user857990 Вся строка представлена ​​в $ 0 в AWK.
Жюльен Гренье

2

Без какой-либо итерации с опцией --line-buffered grep:

your_command | grep --line-buffered "your search"

Пример из реальной жизни с выходными данными команды отладки маршрутизатора Symfony PHP Framework для grep всех связанных с «api» маршрутов:

php bin/console d:r | grep --line-buffered "api"

Единственное решение, которое работает, если your_command является долгосрочным (например tail -f some.log, в моем случае) ...
Izkata

0

Часто порядок обработки не имеет значения. GNU Parallel сделан для этой ситуации:

grep xyz abc.txt | parallel echo do stuff to {}

Если вы обрабатываете больше похоже на:

grep xyz abc.txt | myprogram_reading_from_stdin

и myprogramмедленно, тогда вы можете запустить:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin

0

Переберите результаты grep с помощью цикла while / read. Подобно:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done

0

Для тех, кто ищет однострочник:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.