Jak grepować zawartość po wzorze?


81

Biorąc pod uwagę plik, na przykład:

potato: 1234
apple: 5678
potato: 5432
grape: 4567
banana: 5432
sushi: 56789

Chciałbym grepować dla wszystkich wierszy, które zaczynają się od, potato:ale tylko wyprowadzam tylko następujące liczby potato:. W powyższym przykładzie wynik będzie wyglądał tak:

1234
5432

Jak mogę to zrobić?

Odpowiedzi:


113
grep 'potato:' file.txt | sed 's/^.*: //'

grepszuka dowolnego wiersza zawierającego ciąg potato:, a następnie dla każdego z tych wierszy sedzastępuje ( s///- podstawia) dowolny znak ( .*) od początku wiersza ( ^) do ostatniego wystąpienia ciągu :(dwukropek, po którym następuje spacja) pustym string ( s/...//- zastąp pierwszą część drugą częścią, która jest pusta).

lub

grep 'potato:' file.txt | cut -d\   -f2

Dla każdej linii, która zawiera potato:, cutpodzieli linię na wiele pól oddzielonych spacją ( -d\- d= separator, \= znak spacji ze znakiem ucieczki, coś podobnego -d" "też by zadziałało) i wypisze drugie pole każdej takiej linii ( -f2).

lub

grep 'potato:' file.txt | awk '{print $2}'

Dla każdego wiersza, który zawiera potato:, awkwypisze drugie pole ( print $2), które jest domyślnie rozdzielone spacjami.

lub

grep 'potato:' file.txt | perl -e 'for(<>){s/^.*: //;print}'

Wszystkie wiersze, które zawierają, potato:są wysyłane do skryptu Perl inline ( -e), który pobiera wszystkie wiersze z , a następnie dla każdego z tych wierszy dokonuje takiego samego podstawienia jak w pierwszym przykładzie powyżej, a następnie drukuje go.stdin

lub

awk '{if(/potato:/) print $2}' < file.txt

Plik jest wysyłany przez stdin( < file.txtwysyła zawartość pliku przez stdinpolecenie po lewej stronie) do awkskryptu, który dla każdego wiersza zawierającego potato:( if(/potato:/)zwraca wartość true, jeśli wyrażenie regularne /potato:/pasuje do bieżącego wiersza), wypisuje drugie pole, zgodnie z opisem powyżej.

lub

perl -e 'for(<>){/potato:/ && s/^.*: // && print}' < file.txt

Plik jest wysyłany za pośrednictwem stdin( < file.txtpatrz wyżej) do skryptu Perl, który działa podobnie do powyższego, ale tym razem upewnia się również, że każda linia zawiera ciąg potato:( /potato:/jest to wyrażenie regularne, które pasuje, jeśli bieżąca linia zawiera potato:, robi ( &&), a następnie przechodzi do zastosowania wyrażenia regularnego opisanego powyżej i wyświetla wynik).


3
Nie ma potrzeby stosowania dwóch procesów i rury. Poszedłbym na awk '$1 ~ /potato/ { print $2 }' file.txt.
musiphil

2
Awk byłby bardziej idiomatycznieawk '/potato:/ {print $2}'
Benjamin W.

Skrypty Perla mogą odnieść korzyściperl -pe
tripleee

60

Lub użyj asercji wyrażenia regularnego: grep -oP '(?<=potato: ).*' file.txt


4
Wypróbowałem kilka linijek z zaakceptowanej odpowiedzi powyżej, ale wydaje mi się, że ta odpowiedź dokładniej rozwiązuje pytanie.
Jake88,

3
Pewne wyjaśnienie: Opcja -ooznacza drukowanie tylko pasującej części wiersza. Podczas gdy -Pwnioskuje o wyrażeniu regularnym zgodnym z Perlem, które jest dodatnim wyrażeniem regularnym typu lookbehind(?<=string) .
Serge Stroobandt

9
sed -n 's/^potato:[[:space:]]*//p' file.txt

Można myśleć o Grepie jako o ograniczonym Sed lub o Sedie jako o uogólnionym Grepie. W tym przypadku Sed to dobre, lekkie narzędzie, które robi to, co chcesz - chociaż oczywiście istnieje również kilka innych rozsądnych sposobów, aby to zrobić.


2

Spowoduje to wydrukowanie wszystkiego po każdym dopasowaniu, tylko w tej samej linii:

perl -lne 'print $1 if /^potato:\s*(.*)/' file.txt

Zrobi to samo, z wyjątkiem tego, że wypisze również wszystkie kolejne wiersze:

perl -lne 'if ($found){print} elsif (/^potato:\s*(.*)/){print $1; $found++}' file.txt

Używane są następujące opcje wiersza polecenia:

  • -n pętla wokół każdego wiersza pliku wejściowego
  • -l usuwa nowe wiersze przed przetworzeniem i dodaje je później
  • -e wykonać kod perla

2
grep -Po 'potato:\s\K.*' file

-P użyć wyrażenia regularnego Perla

-o aby wyświetlić tylko dopasowanie

\s aby dopasować spację po potato:

\K pominąć dopasowanie

.* aby dopasować resztę ciągu (ów)


1

Możesz użyć grep, jak podają inne odpowiedzi. Ale nie potrzebujesz grep, awk, sed, perl, cut ani żadnego zewnętrznego narzędzia. Możesz to zrobić za pomocą czystego basha.

Spróbuj tego (średniki są tam, aby umożliwić umieszczenie wszystkiego w jednym wierszu):

$ while read line;
  do
    if [[ "${line%%:\ *}" == "potato" ]];
    then
      echo ${line##*:\ };
    fi;
  done< file.txt

## mówi bashowi, aby usunął najdłuższe dopasowanie „:” w linii $ od początku.

$ while read line; do echo ${line##*:\ }; done< file.txt
1234
5678
5432
4567
5432
56789

lub jeśli chciałeś mieć klucz zamiast wartości, %% mówi bashowi, aby usunął najdłuższe dopasowanie „:” w linii $ od końca.

$ while read line; do echo ${line%%:\ *}; done< file.txt
potato
apple
potato
grape
banana
sushi

Podciąg do podziału to „: \”, ponieważ znak spacji musi być poprzedzony ukośnikiem odwrotnym.

Więcej takich znajdziesz w projekcie dokumentacji Linuksa .


while readjest bardzo powolny; użycie zewnętrznego narzędzia będzie w rzeczywistości znacznie szybsze, o ile wybierzesz takie z buforowanym I / O (tj. praktycznie każde z wymienionych w tej odpowiedzi i wiele innych).
tripleee

Powinieneś także używać, read -rchyba że potrzebujesz jakiegoś raczej nieznośnego zachowania sprzed POSIX.
tripleee

0

Nowoczesny BASH obsługuje wyrażenia regularne:

while read -r line; do
  if [[ $line =~ ^potato:\ ([0-9]+) ]]; then
    echo "${BASH_REMATCH[1]}"
  fi
done
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.