Istnieją dwa sposoby interpretacji tego pytania; Zajmę się obydwoma przypadkami. Możesz chcieć wyświetlić linie:
- które zawierają ciąg czterech cyfr, który sam nie jest częścią dłuższej sekwencji cyfr, lub
- który zawiera czterocyfrową sekwencję, ale nie sekwencję cyfr (nawet osobno).
Na przykład wyświetli się (1) 1234a56789
, ale (2) nie.
Jeśli chcesz wyświetlić wszystkie wiersze zawierające ciąg czterech cyfr, który sam nie jest częścią żadnej dłuższej sekwencji cyfr, jednym ze sposobów jest:
grep -P '(?<!\d)\d{4}(?!\d)' file
Korzysta z wyrażeń regularnych Perla , obsługiwanych przez Ubuntu grep
( GNU grep ) -P
. Nie będzie pasować do tekstu podobnego 12345
ani nie będzie pasować do 1234
ani tych, 2345
które są jego częścią. Ale będzie to dopasować 1234
in 1234a56789
.
W wyrażeniach regularnych Perla:
\d
oznacza dowolną cyfrę (to krótki sposób powiedzieć [0-9]
lub [[:digit:]]
).
x{4}
dopasowuje x
4 razy. ( {
}
składnia nie jest specyficzna dla wyrażeń regularnych Perla; jest również w rozszerzonych wyrażeniach regularnych poprzez grep -E
.) Tak \d{4}
samo jest z \d\d\d\d
.
(?<!\d)
jest twierdzeniem negatywnym o zerowej szerokości. Oznacza to „chyba że poprzedza je \d
”.
(?!\d)
jest twierdzeniem o negatywnej perspektywie o zerowej szerokości. Oznacza „chyba, że następuje \d
”.
(?<!\d)
i (?!\d)
nie dopasowuj tekstu poza ciągiem czterech cyfr; zamiast tego zapobiegną (gdy zostaną użyte razem), aby nie dopasować do siebie ciągu czterech cyfr, jeśli jest on częścią dłuższej sekwencji cyfr.
Samo spojrzenie wstecz lub po prostu nie jest wystarczające, ponieważ czterocyfrowa podsekwencja znajdująca się najbardziej na prawo lub na lewo byłaby nadal dopasowywana.
Jedną z korzyści korzystania z asercji z wyprzedzeniem i z wyprzedzeniem jest to, że wzorzec pasuje tylko do samych czterocyfrowych sekwencji, a nie do otaczającego tekstu. Jest to przydatne podczas korzystania z wyróżniania kolorów (z --color
opcją).
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
Domyślnie w Ubuntu każdy użytkownik ma alias grep='grep --color=auto'
w swoim ~.bashrc
pliku . Tak więc podświetlanie kolorów jest automatycznie wykonywane po uruchomieniu prostej komendy rozpoczynającej się od grep
(to jest wtedy, gdy aliasy są rozwinięte), a standardowym wyjściem jest terminal (to właśnie sprawdza). Mecze są zazwyczaj podświetlane w odcieniu czerwieni (zbliżonym do cynobru ), ale pokazałem to pogrubioną kursywą. Oto zrzut ekranu:--color=auto
Możesz nawet grep
drukować tylko pasujący tekst, a nie całą linię, dzięki -o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
Alternatywny sposób, bez asertywności i asercji
Jeśli jednak:
- potrzebujesz polecenia, które będzie działać również w systemach, w których
grep
nie obsługuje -P
lub w inny sposób nie chce używać wyrażenia regularnego Perl, i
- nie musisz specjalnie dopasowywać czterech cyfr - co zwykle ma miejsce, jeśli Twoim celem jest po prostu wyświetlanie wierszy zawierających dopasowania, i
- są w porządku z rozwiązaniem, które jest nieco mniej eleganckie
... możesz to osiągnąć za pomocą rozszerzonego wyrażenia regularnego :
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
Dopasowuje cztery cyfry i otaczający je znak inny niż cyfra - lub początek lub koniec linii. Konkretnie:
[0-9]
dopasowuje dowolną cyfrę (jak [[:digit:]]
lub \d
w wyrażeniach regularnych Perla) i {4}
oznacza „cztery razy”. Tak [0-9]{4}
dopasowuje sekwencję czterocyfrowy.
[^0-9]
znaków nie pasuje w zakresie 0
through 9
. Jest to równoważne [^[:digit:]]
(lub \D
w wyrażeniach regularnych Perla).
^
, gdy nie pojawia się w [
]
nawiasach, dopasowuje początek linii. Podobnie, $
dopasowuje koniec linii.
|
oznacza lub nawiasy są do grupowania (jak w algebrze). Tak więc (^|[^0-9])
dopasowuje początek linii lub znak niecyfrowy, a ($|[^0-9])
dopasowuje koniec linii lub znak niecyfrowy.
Tak więc dopasowania występują tylko w wierszach zawierających czterocyfrową sekwencję ( [0-9]{4}
), która jest jednocześnie:
- na początku wiersza lub poprzedzony cyfrą (
(^|[^0-9])
) i
- na końcu linii lub po niej następuje cyfra (
($|[^0-9])
).
Jeśli z drugiej strony chcesz wyświetlić wszystkie wiersze zawierające czterocyfrową sekwencję, ale nie zawierają one żadnej sekwencji większej niż cztery cyfry (nawet jednej oddzielnej od innej sekwencji tylko czterech cyfr), to koncepcyjnie twoja celem jest znalezienie linii, które pasują do jednego wzoru, ale nie do drugiego.
Dlatego, nawet jeśli wiesz, jak to zrobić za pomocą jednego wzoru, sugeruję coś takiego za pomocą Matta drugiego sugestię, grep
ing dla dwóch wzorów oddzielnie.
Robiąc to, nie korzystasz z żadnej z zaawansowanych funkcji wyrażeń regularnych Perla, więc możesz nie chcieć ich używać. Ale zgodnie z powyższym stylem, oto skrócenie rozwiązania matowego przy użyciu \d
(i nawiasów klamrowych) zamiast [0-9]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
Ponieważ używa [0-9]
, sposób Matta jest bardziej przenośny - będzie działał na systemach, w których grep
nie obsługuje wyrażeń regularnych Perla. Jeśli użyjesz [0-9]
(lub [[:digit:]]
) zamiast \d
, ale nadal {
}
będziesz używać , uzyskasz przenośność Matta nieco bardziej zwięźle:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
Alternatywny sposób, z jednym wzorem
Jeśli naprawdę wolisz takie grep
polecenie
- używa pojedynczego wyrażenia regularnego (nie dwóch
grep
oddzielonych potokiem , jak wyżej)
- aby wyświetlić wiersze zawierające co najmniej jedną sekwencję czterech cyfr,
- ale bez sekwencji pięciu (lub więcej) cyfr,
- i nie masz nic przeciwko dopasowaniu całej linii, nie tylko cyfr (prawdopodobnie nie masz nic przeciwko temu)
... możesz użyć:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
Te -x
marki flag grep
wyświetlać tylko linie gdzie cały dopasowania linii (raczej niż jakikolwiek wiersz zawierający mecz).
Użyłem wyrażenia regularnego Perla, ponieważ uważam, że w tym przypadku zwięzłość \d
i \D
znacznie zwiększam jasność. Ale jeśli potrzebujesz czegoś przenośnego dla systemów, w których grep
nie obsługuje -P
, możesz je zastąpić za pomocą [0-9]
i [^0-9]
(lub za pomocą [[:digit:]]
i [^[:digit]]
):
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
Sposób działania tych wyrażeń regularnych jest następujący:
W środku \d{4}
lub [0-9]{4}
odpowiada jednej sekwencji czterech cyfr. Możemy mieć więcej niż jeden z nich, ale musimy mieć co najmniej jeden.
Po lewej stronie, (\d{0,4}\D)*
lub ([0-9]{0,4}[^0-9])*
dopasowuje zero lub więcej ( *
) przypadki nie więcej niż czterech cyfr, a następnie non-cyfry. Zero cyfr (tj. Nic) jest jedną z możliwości dla „nie więcej niż czterech cyfr”. Odpowiada to (a) pustemu ciągowi lub (b) dowolnemu ciągowi, który kończy się cyfrą i nie zawiera żadnych sekwencji dłuższych niż cztery cyfry.
Ponieważ tekst znajdujący się bezpośrednio po lewej stronie centralnej \d{4}
(lub [0-9]{4}
) musi być pusty lub kończyć się cyfrą, zapobiega to \d{4}
dopasowaniu czterech cyfr, które mają inną (piątą) cyfrę po lewej stronie.
Po prawej stronie (\D\d{0,4})*
lub ([^0-9][0-9]{0,4})*
dopasowuje zero lub więcej ( *
) wystąpień niecyfrowych, po których następują nie więcej niż cztery cyfry (które, podobnie jak poprzednio, mogą mieć cztery, trzy, dwie, jedną lub nawet żadną). Odpowiada to (a) pustemu ciągowi lub (b) dowolnemu ciągowi rozpoczynającemu się od cyfr i niezawierającym żadnych sekwencji dłuższych niż cztery cyfry.
Ponieważ tekst znajdujący się bezpośrednio po prawej stronie centralnej \d{4}
(lub [0-9]{4}
) musi być pusty lub zaczynać się cyfrą, zapobiega to \d{4}
dopasowaniu czterech cyfr, które mają inną (piątą) cyfrę tuż po prawej stronie.
Zapewnia to, że gdzieś występuje czterocyfrowa sekwencja i że nigdzie nie występuje sekwencja pięciu lub więcej cyfr.
Nie jest źle ani źle to robić w ten sposób. Ale być może najważniejszym powodem do rozważenia tej alternatywy jest wyjaśnienie korzyści z używania (lub podobnego) zamiast, jak sugerowano powyżej i w odpowiedzi Matta .grep -P '\d{4}' file | grep -Pv '\d{5}'
W ten sposób staje się jasne, że Twoim celem jest wybranie wierszy zawierających jedną rzecz, ale nie inną. Ponadto składnia jest prostsza (dlatego może być szybciej zrozumiana przez wielu czytelników / opiekunów).
1234a12345
być wyświetlany, czy nie?