Grep wyszukuje dwa słowa w linii


46

Próbowałem znaleźć sposób na odfiltrowanie wiersza zawierającego słowa „cytryna” i „ryż”. Wiem, jak znaleźć „cytrynę” lub „ryż”, ale nie dwa z nich. Nie muszą znajdować się obok siebie, tylko jeden wiersz tekstu.


1
Aby znaleźć wszystkie ciągi znaków w pliku, możesz uruchomić grep w pętli FOR: unix.stackexchange.com/a/462445/43233
Noam Manos

Odpowiedzi:


62

„Oba w tej samej linii” oznaczają „ryż”, po których następują losowe znaki, po których następuje „cytryna” lub odwrotnie.

W wyrażeniu regularnym czyli rice.*lemonlub lemon.*rice. Możesz to połączyć za pomocą |:

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

Jeśli chcesz użyć normalnego wyrażenia regularnego zamiast rozszerzonych ( -E), potrzebujesz odwrotnego ukośnika przed |:

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

Aby uzyskać więcej słów, które szybko stają się nieco dłuższe i zwykle łatwiej jest używać wielu wywołań grep, na przykład:

grep rice some_file | grep lemon | grep chicken

Twoja ostatnia linia to spójnik, a nie rozłączenie, nie? To znaczy: grep riceznajduje wiersze zawierające rice. Jest karmiony, w grep lemonktórym znajdą się tylko linie zawierające cytrynę ... i tak dalej. Podczas gdy PO - podobnie jak poprzednie odpowiedzi - zezwala na każdy z [ryż | cytryna | kurczak]
javadba


@Florian Diesch - Umysł wyjaśniający, dlaczego |trzeba uciekać grep? Dzięki!
zbieg

1
@ uciekinier egrepużywa rozszerzonego wyrażenia regularnego, gdzie |jest rozumiane jako logika OR. grepdomyślnie jest wyrażeniem regularnym, gdzie \|OR
Sergiy Kolodyazhnyy

Jak podano na stronie greppodręcznika, egrepjest przestarzałe i powinno zostać zastąpione przez grep -E. Pozwoliłem sobie odpowiednio edytować odpowiedź.
deser

26

Możesz przesłać dane wyjściowe pierwszego polecenia grep do innego polecenia grep, które pasowałoby do obu wzorców. Możesz więc zrobić coś takiego:

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

lub,

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

Przykład:

Dodajmy trochę zawartości do naszego pliku:

$ 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

Co zawiera plik:

$ 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.

Teraz grep, co chcemy:

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

Otrzymujemy tylko linie, w których oba wzorce pasują. Możesz to rozszerzyć i przesłać dane wyjściowe do innego polecenia grep w celu uzyskania dalszych dopasowań „AND”.


21

Chociaż pytanie dotyczy „grep”, pomyślałem, że pomocne może być opublikowanie prostego rozwiązania „awk”:

awk '/lemon/ && /rice/'

Można to łatwo rozszerzyć o więcej słów lub innych wyrażeń logicznych oprócz „i”.


11

Innym pomysłem na znalezienie dopasowań w dowolnej kolejności jest użycie:

grep z opcją -P (Perl-Compatibility) i regex pozytywnego wyglądu(?=(regex)) :

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

lub możesz użyć poniżej:

grep -P '(?=.*?rice)(?=.*?lemon)' infile
  • Te .*?środki dopasowania żadnych znaków ., że zdarzenia zero lub więcej razy *, gdy są opcjonalne, a następnie wzór ( ricelub lemon). ?Czyni wszystko Opcjonalnie przed (czyli zero lub jeden raz wszystkiego dopasowane .*)

(?=pattern): Positive Lookahead: Pozytywna konstrukcja lookahead to para nawiasów, z nawiasem otwierającym, po którym następuje znak zapytania i znak równości.

To zwróci wszystkie wiersze z zawiera oba lemoni ricew losowej kolejności. Pozwoli to również uniknąć używania |s i podwójnych greps.


Linki zewnętrzne: Zaawansowane tematy Grep Pozytywne spojrzenie w przyszłość - GREP dla projektantów



1

Jeśli przyznamy, że udzielenie odpowiedzi, która nie jest grepoparta, jest akceptowalne, podobnie jak powyższa odpowiedź oparta na awk, zaproponowałbym prostą perllinię, taką jak:

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

Wyszukiwanie może ignorować wielkość liter w przypadku niektórych / wszystkich słów takich jak /lemon/i and /rice/i. Na większości maszyn Unix / Linux perl jest instalowany, a także awk.


Odmowa !!! ;) Ponieważ to nie ma sensu .. :)
An0n

0

Oto skrypt automatyzujący rozwiązanie grep piping:

#!/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
Prawdopodobnie powinno to zostać zaimplementowane za pomocą funkcji rekurencyjnej, zamiast budowania łańcucha poleceń i wprowadzania evalgo, co łatwo się
psuje

@muru Możesz zaproponować edycję. Doceniam ten komentarz.
Jeff

1
Edytowanie tego będzie zbyt przerośnięte, więc tego nie zrobię. Jeśli chcesz go dodać, oto, jak wyobrażam sobie, że powinien on wyglądać: paste.ubuntu.com/23915379
muru
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.