N basamaklı gruplar için grep, ancak n'den büyük değil


33

Linux öğreniyorum ve kendi başıma çözemediğim bir sorunum var. İşte burada:

arka arkaya 4 sayı içeren ancak 4'ten fazla olmayan bir dosyadan satır grep.

Buna nasıl yaklaşacağımdan emin değilim. Belirli numaraları arayabilirim, ancak dizgede tutamazlar.


2
Gibi bir çizgi 1234a12345görüntülenmeli mi, görüntülenmemeli mi?
Eliah Kagan

@Buddha sorunuzu bir örnek ile birlikte açıklamanız gerekir.
Avinash Raj

sayılardan önce satır çapasının boşluğu veya başlangıcı varsa ve ardından satır çapasının boşluğu veya sonu geliyorsa, sözcük sınırlarını kullanabilirsiniz. \b\d{4}\b
Avinash Raj

1
Bu soru açıkça grep kullanımıyla ilgili olarak düzenli ifadelerle ilgili bazı sorulardan farklıdır . Ubuntu'da Unix yardımcı programlarının grep, sed ve awk gibi kullanımı hakkındaki sorular burada her zaman iyi kabul edildi. Bazen insanlar yanlış bir araçla nasıl iş yapabileceğini sorar ; o zaman bağlam eksikliği büyük bir sorundur, ama burada olan bu değil. Bu, konuyla ilgili, yararlı bir şekilde cevaplanacak kadar açık, topluluğumuz için faydalı ve daha fazla yanıtın önlenmesinde ya da silinmeye ya da taşınmaya doğru itilmesinin faydası yok. Yeniden açmak için oy kullanıyorum.
Eliah Kagan

1
Çok teşekkürler çocuklar, bu kadar geri bildirim alacağıma dair hiçbir fikrim yoktu. Aradığım cevap bu: grep -E '(^ ​​| [^ 0-9]) [0-9] {4} ($ | [^ 0-9])' dosyası. Komut bunun gibi bir ipi çekebilmelidir (ki öyle): abc1234abcd99999
Buddha

Yanıtlar:


52

Bu soruyu yorumlamanın iki yolu vardır; Her iki vakayı da ele alacağım. Satırları görüntülemek isteyebilirsiniz:

  1. artık herhangi bir artık basamak sırasının parçası olmayan dört basamaklı bir sekans içeren veya
  2. Bu, dört basamaklı bir diziyi içerir ancak artık basamak dizisi içermez (ayrı olarak bile değil).

Örneğin, (1) görüntülenir 1234a56789, ancak (2) görüntülenmez .


Dört basamaktan oluşan bir diziyi içeren tüm satırları, artık herhangi bir basamaklı dizinin bir parçası olmayan görüntülemek istiyorsanız, bunun bir yolu:

grep -P '(?<!\d)\d{4}(?!\d)' file

Bu , Ubuntu'nun ( GNU grep ) desteklediği Perl düzenli ifadelerini kullanır . Gibi bir metinle eşleşmeyecek, onunla aynı olan veya onun bir parçası olanla eşleşmeyecektir . Ama maç olacak in .grep-P123451234234512341234a56789

Perl'de düzenli ifadeler:

  • \dherhangi bir rakam (demek [0-9]veya söylemek için kısa bir yoldur [[:digit:]]) anlamına gelir .
  • x{4}x4 kez eşleşir . ( { }sözdizimi Perl düzenli ifadelerine özgü değildir; uzatılmış düzenli ifadelerde grep -Ede kullanılır.) Öyle \d{4}de aynı \d\d\d\d.
  • (?<!\d)sıfır genişlikte bir negatif geriye dönük iddia. "Öncesinde olmadıkça" demek \d.
  • (?!\d)sıfır genişlikte negatif ileriye dönük bir iddiadır. "Takip etmediği sürece" demek \d.

(?<!\d)ve (?!\d)dört basamaklı dizinin dışındaki metinle eşleşmeyin; bunun yerine (birlikte kullanıldığında), daha uzun bir rakam dizisinin bir parçasıysa, dört basamaklı bir dizinin kendisinden eşleşmesini önler.

En arkadan veya en ileriden yalnızca geriye doğru bakmak kullanmak yetersizdir, çünkü en sağdaki veya en soldaki dört basamaklı sıra yine de eşleşecektir.

Geriye dönük ve ileriye dönük iddiaları kullanmanın bir yararı, deseninizin çevresindeki metinle değil, yalnızca dört basamaklı dizilerle eşleşmesidir. Renk vurgulamayı kullanırken ( --colorseçenekle birlikte) bu yardımcı olur.

ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4

Ubuntu'da varsayılan olarak, her kullanıcının alias grep='grep --color=auto'kendi ~.bashrcdosyasında vardır . Eğer ile başlayan basit bir komut çalıştırdığınızda otomatik vurgulama rengi elde Yani grep(zaman bu takma adlar genişletilir) ve standart çıktı olan bir terminal (bu nedir denetler). Maçlar tipik olarak kırmızı bir gölgede vurgulanır ( vermilyona yakın ), ancak bunu italik kalın olarak gösterdim. İşte bir ekran görüntüsü:--color=auto
Bu grep komutunu 12345abc789d0123e4 ile çıkış olarak, 0123 kırmızı ile vurgulanmış olarak gösteren ekran görüntüsü.

Ve ayrıca grep, satırın tamamını değil, yalnızca eşleşen metinleri yazdırabilirsiniz -o:

ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123

Arkası Olmadan ve İleriye Doğru Varma Beyanları Olmadan Alternatif Yol

Ancak, eğer:

  1. Perl düzenli ifadesini kullanmak istemediği veya grepdesteklemediği sistemler üzerinde de çalışan bir komuta ihtiyacınız var ve-P
  2. Özel olarak dört rakamla eşleşmenize gerek yoktur - bu genellikle amacınız yalnızca eşleşmeleri içeren satırları görüntülemekse böyle olur ve
  3. biraz daha az zarif bir çözüm ile tamam

... o zaman bunun yerine genişletilmiş düzenli bir ifadeyle bunu başarabilirsiniz :

grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file

Bu, dört hane ve sayı olmayan karakter - veya çizginin başına veya sonuna - çevrelenerek eşleşir. özellikle:

  • [0-9]herhangi bir rakamla eşleşir (örneğin [[:digit:]]veya \dPerl normal ifadelerinde) ve {4}"dört kez" anlamına gelir. Böylece [0-9]{4}dört basamaklı bir dizilimle eşleşir.
  • [^0-9]değil aralığında karakterle eşleşir 0through 9. Eşittir [^[:digit:]](veya \DPerl düzenli ifadelerinde).
  • ^, [ ]parantez içinde görünmediğinde, satırın başlangıcıyla eşleşir. Benzer şekilde, $bir satırın sonuyla eşleşir.
  • |araçlar veya ve parantezler gruplama içindir (cebirdeki gibi). Böylece (^|[^0-9]), satırın başlangıcı veya rakam olmayan bir karakterle ($|[^0-9])eşleşir, satırın sonu veya rakam olmayan bir karakterle eşleşir.

Dolayısıyla eşleşmeler yalnızca [0-9]{4}aynı anda olan dört basamaklı bir diziyi ( ) içeren satırlarda gerçekleşir :

  • satırın başlangıcında veya öncesinde bir rakam olmayan ( (^|[^0-9])) ve
  • satırın sonunda veya rakamsız ( ($|[^0-9])) ile izlenir .

Öte yandan, dört basamaklı bir sıra içeren tüm satırları görüntülemek istiyorsanız, ancak dört basamaktan daha fazla bir sıra içermiyorsa (yalnızca dört basamaklı başka bir sıradan ayrı bir satır bile), ardından kavramsal olarak amaç, bir desenle eşleşen ama bir diğeriyle eşleşen çizgileri bulmak.

Bu nedenle, tek bir desenle bunu nasıl bilmek bile, şöyle bir şey kullanarak öneririm mat en , ikinci öneri grepayrı iki kalıpları için ing.

Bunu yaparken Perl düzenli ifadelerinin gelişmiş özelliklerinden hiçbirinden tam olarak yararlanamazsınız, bu yüzden bunları kullanmamayı tercih edebilirsiniz. Ancak yukarıdaki stile uygun olarak, şunun yerine (ve diş telleri) kullanarak mat'ın çözümünün kısaltılması :\d[0-9]

grep -P '\d{4}' file | grep -Pv '\d{5}'

Kullandığı yana [0-9], mat yoludur daha taşınabilir - bu sistemlerde çalışacak grepPerl düzenli ifadeler desteklemez. Kullanmak yerine [0-9](veya [[:digit:]]) kullanırsanız \d, ancak kullanmaya devam { }ederseniz, mat'ın taşınabilirliğini biraz daha net bir şekilde elde edersiniz:

grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'

Tek Desenli Alternatif Yol

Eğer gerçekten bir grepkomut tercih ederseniz

  1. Tek bir düzenli ifade kullanır ( yukarıdaki gibi grepbir boru ile iki saniye değil )
  2. en az bir dört basamak dizisi içeren satırları görüntülemek,
  3. fakat beş (veya daha fazla) hane dizisi içermez,
  4. ve sadece rakamları değil, tüm çizgiyi eşleştirmeyi umursamıyorsunuz (muhtemelen buna aldırmazsınız)

... sonra kullanabilirsiniz:

grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file

-xBayrak markaları grepsatırları gösterecektir burada tüm çizgi maçlar (yerine bir çizgi içeren bir maç).

Bir Perl düzenli ifadesi kullandım çünkü bence bu konudaki açıklığı \dve \Dnetliğini önemli ölçüde arttırıyor. Ancak , grepdesteklemeyen sistemlerde taşınabilir bir şeye ihtiyacınız olursa -P, bunları [0-9]ve [^0-9](veya [[:digit:]]ve ile [^[:digit]]) ile değiştirebilirsiniz:

grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file

Bu normal ifadelerin çalışma şekli:

  • Ortada \d{4}veya [0-9]{4}dört basamaklı bir sekansla eşleşiyor. Bunlardan birden fazlasına sahip olabiliriz, ancak en az birine sahip olmamız gerekir.

  • Solda (\d{0,4}\D)*veya ([0-9]{0,4}[^0-9])*sıfır veya daha fazla ( *) örnekle, dört basamaktan daha fazla olmayan, ardından rakam olmayanlar. Sıfır rakamlar (yani hiçbir şey) "dört rakamdan fazla değil" için bir olasılıktır. Bu (a) boş dize veya (b) rakamsız biten herhangi bir dize ve dörtten fazla rakam içermeyen dizilerle eşleşir .

    Hemen merkezin \d{4}(veya [0-9]{4}) solundaki metin ya boş ya da rakamsız bir bit ile bitmesi gerektiğinden, bu, merkezin \d{4}solunda başka bir (beşinci) rakam olan dört rakamla eşleşmesini önler .

  • Sağda (\D\d{0,4})*veya ([^0-9][0-9]{0,4})*sıfır veya daha fazla ( *) rakamsız bir örnekle eşleşir ve ardından dört rakamdan fazla olmaz (daha önce olduğu gibi dört, üç, iki, bir veya hatta hiçbiri olmayabilir). Bu maçlar , (a) boş dize veya (b) herhangi bir dizi başlayan olmayan bir rakam olarak ve en fazla dört basamaklı bir dizilerini içermeyen.

    Merkezin \d{4}(veya [0-9]{4}) hemen sağındaki metin ya boş olmalı ya da rakamsız bir rakamla başlamalıdır, bu, merkezin \d{4}sağında başka bir (beşinci) rakam olan dört rakamla eşleşmesini önler .

Bu, bir yerde dört basamaklı bir dizinin bulunmasını ve hiçbir yerde beş veya daha fazla basamak bulunmamasını sağlar.

Bu şekilde yapmak kötü ya da yanlış değil. Ancak, belki de bu alternatifi değerlendirmenin en önemli nedeni , yukarıda ve Matt'in cevabında önerildiği gibi bunun yerine (veya benzerini) kullanmanın faydasını netleştirmesidir .grep -P '\d{4}' file | grep -Pv '\d{5}'

Bu şekilde amacınız, bir şeyi içeren ama bir başkasını içermeyen satırları seçmektir. Ayrıca, sözdizimi daha basittir (bu yüzden birçok okuyucu / bakıcı tarafından daha hızlı anlaşılabilir).


9

Bu size arka arkaya 4 sayı gösterecek

grep '[0-9][0-9][0-9][0-9][^0-9]' file

^ 'Nın değil demektir.

Bununla ilgili bir sorun var, nasıl düzelteceğimi bilmiyorum ... eğer sayı satırın sonundaysa o zaman ortaya çıkmayacak.

Bu çirkin sürüm ancak bu durumda işe yarayacak

grep '[0-9][0-9][0-9][0-9]' file | grep -v [0-9][0-9][0-9][0-9][0-9]

oops, egrep olmak zorunda değildi
matt

2
İlki yanlıştır - bulur a12345b, çünkü eşleşir 2345b.
Volker Siegel,

0

Eğer grepPerl düzenli ifadeler (desteklemez -P), aşağıdaki kabuk komutu kullanın:

grep -w "$(printf '[0-9]%.0s' {1..4})" file

Nerede printf '[0-9]%.0s' {1..4}4 kez üretecek [0-9]. Bu yöntem, uzun rakamlara sahipseniz ve deseni tekrarlamak istemiyorsanız kullanışlıdır (yalnızca 4aramak istediğiniz rakam sayınızla değiştirin).

Kullanarak -wbütün kelimeleri arayacaktır. Ancak, örneğin alfanümerik dizelerle ilgileniyorsanız , örneğin kalıbın sonuna 1234aekleyin [^0-9].

grep "$(printf '[0-9]%.0s' {1..4})[^0-9]" file

Kullanımı $()temelde bir komut ikamesidir . Deseni nasıl tekrar ettiğini görmek için bu postayı kontrol edin printf.


0

Aşağıdaki komutu file, sisteminizdeki gerçek dosya adı ile değiştirerek deneyebilirsiniz :

grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file

Bu öğreticiyi , grep komutunun daha fazla kullanımı için de kontrol edebilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.