Греп пошук двох слів у рядку


46

Я намагався знайти спосіб відфільтрувати рядок, в якому є слово "лимон" та "рис". Я знаю, як знайти "лимон" або "рис", але не їх два. Їм не потрібно бути поруч з одним, лише один і той же рядок тексту.


1
Щоб знайти всі рядки у файлі, ви можете запустити grep у циклі FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Відповіді:


62

"Обоє в одному рядку" означають "рис", за яким слідують випадкові символи, а потім "лимон" або навпаки ".

У регулярному вираженні це rice.*lemonабо lemon.*rice. Ви можете комбінувати це за допомогою |:

grep -E 'rice.*lemon|lemon.*rice' some_file

Якщо ви хочете використовувати звичайний регулярний вимір замість розширеного ( -E), вам потрібен зворотний нахил перед |:

grep 'rice.*lemon\|lemon.*rice' some_file

Для отримання додаткових слів, які швидко стають трохи тривалими, і, як правило, простіше використовувати кілька дзвінків grep, наприклад:

grep rice some_file | grep lemon | grep chicken

Ваш останній рядок - це сполучник, а не диз'юнкція, ні? На кмітливість: grep riceрядки знахідки, що містять rice. Він подається в grep lemonякий знайде лише рядки, що містять лимон .. і так далі. Тоді як ОП - як і ваші попередні відповіді - дозволяють будь-яку з [рис | лимон | курятину]
javadba

Версія сценарію: askubuntu.com/a/879253/5696
Jeff

@Florian Diesch - Розум пояснює, чому |потрібно втекти grep? Дякую!
втікач

1
@fugitive egrepвикористовує розширений регулярний вираз, де |це розуміється як АБО логіка. grepза замовчуванням до базового регексу, де \|АБО
Сергій Колодяжний,

Як зазначено на сторінці grep"Російська сторінка", egrepвона застаріла і її слід замінити grep -E. Я взяв свободу відповідно до редагування відповіді.
десерт

26

Ви можете передавати вихід першої команди grep в іншу команду grep, яка відповідатиме обом шаблонам. Отже, ви можете зробити щось на кшталт:

grep <first_pattern> <file_name> | grep <second_pattern>

або,

cat <file_name> | grep <first_pattern> | grep <second_pattern>

Приклад:

Додамо трохи вмісту до нашого файлу:

$ echo "This line contains lemon." > test_grep.txt
$ echo "This line contains rice." >> test_grep.txt
$ echo "This line contains both lemon and rice." >> test_grep.txt
$ echo "This line doesn't contain any of them." >> test_grep.txt
$ echo "This line also contains both rice and lemon." >> test_grep.txt

Що містить файл:

$ cat test_grep.txt 
This line contains lemon.
This line contains rice.
This line contains both lemon and rice.
This line doesn't contain any of them.
This line also contains both rice and lemon.

Тепер давайте поглянемо на те, що ми хочемо:

$ grep rice test_grep.txt | grep lemon
This line contains both lemon and rice.
This line also contains both rice and lemon.

Ми отримуємо лише лінії, де обидва візерунки збігаються. Ви можете поширити це та передати вихід до іншої команди grep для подальших матчів "AND".


21

Хоча питання задає "grep", я подумав, що може бути корисно опублікувати просте "awk" рішення:

awk '/lemon/ && /rice/'

Це можна легко доповнити ще кількома словами або іншими булевими виразами, окрім 'і'.


11

Ще одна ідея пошуку відповідностей у будь-якому порядку:

опція grep з -P (Perl-Compatibility) опцією та позитивним виразним виглядом(?=(regex)) :

grep -P '(?=.*?lemon)(?=.*?rice)' infile

або ви можете використовувати нижче, замість цього:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • Ці .*?кошти , відповідні будь-які символи , .які входження нуль або більше разів , *поки вони НЕ є обов'язковими з подальшим малюнком ( riceабо lemon). ?Робить все Факультативно , перш ніж він (означає нуль або один раз за все відповідає .*)

(?=pattern): Позитивний Lookahead: Позитивна конструкція lookahead - це пара круглих дужок, за круглими дужками, за якими слідує знак питання та знак рівності.

Таким чином, це поверне всі рядки з містить lemonі riceв, і в довільному порядку. Також це дозволить уникнути використання |s та подвоєних greps.


Зовнішні посилання: Розширені теми Grep Positive Lookahead - GREP для дизайнерів



1

Якщо ми визнаємо, що надання відповіді, яка не grepґрунтується, є прийнятною, як і наведена вище відповідь awk, я запропонував би простий perlрядок типу:

$ perl -ne 'print if /lemon/ and /rice/' my_text_file

Пошук може ігнорувати випадок з деякими / всіма словами /lemon/i and /rice/i. На більшості машин Unix / Linux perl встановлюється так само, як і awk.


Відмовились !!! ;) Тому що це не має сенсу .. :)
An0n

0

Ось сценарій для автоматизації рішення греп-трубопроводів:

#!/bin/bash

# Use filename if provided as environment variable, or "foo" as default
filename=${filename-foo}

grepand () {
# disable word splitting and globbing
IFS=
set -f
if [[ -n $1 ]]
then
grep -i "$1" ${filename} | filename="" grepand "${@:2}"
else
# If there are no arguments, assume last command in pipe and print everything
cat
fi
}

grepand "$@"

1
Це, мабуть, має бути реалізовано за допомогою рекурсивної функції замість того, щоб будувати командний рядок та виконувати evalйого, який легко порушується
muru

@muru Не соромтеся запропонувати зміни. Я вдячний за коментар.
Джефф

1
Якщо це редагувати, це буде занадто багато переписати, тому я цього не буду робити. Якщо ви хочете додати його, ось як я думаю, це має виглядати так: paste.ubuntu.com/23915379
muru
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.