İki kelimeden birini içeren ancak ikisini birden olmayan satırları nasıl okurum?


25

grepYalnızca iki kelimeden birini içeren satırları göstermeye çalışıyorum, satırda yalnızca biri görünüyorsa, ancak aynı satırda değilse.

Şimdiye kadar denedim grep pattern1 | grep pattern2 | ...ama beklediğim sonucu alamadım.


(1) “Kelimeler” ve “kalıplar” hakkında konuşuyorsunuz. Hangisi? "Hızlı", "kahverengi" ve "tilki" gibi sıradan kelimeler veya normal ifadeler [a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+? (2) Eğer kelimelerden / kalıplardan biri bir satırda bir defadan fazla görünürse (diğeri görünmüyorsa)? Bu, bir kez görünen kelimeye eşdeğer mi, yoksa çoklu tekrarlamalar olarak mı sayılıyor?
G-Man

Yanıtlar:


59

Bundan başka bir araç grepgitmek için yoludur.

Perl kullanarak, örneğin, komut olacaktır:

perl -ne 'print if /pattern1/ xor /pattern2/'

perl -nestdin'in her satırı üzerinde verilen komutu çalıştırır; bu durumda eğer eşleşirse satırı basar, /pattern1/ xor /pattern2/diğer bir deyişle bir deseni eşleştirir, diğerini seçmez (özel veya).

Bu, desen için her iki sırada da çalışır ve birden fazla çağrı yapılmasından daha iyi bir performansa sahip olmalı grepve daha az daktilo yazması gerekir.

Veya, daha kısa, awk ile:

awk 'xor(/pattern1/,/pattern2/)'

veya sahip olmayan awk sürümleri için xor:

awk '/pattern1/+/pattern2/==1`

4
Güzel - xorAwk yalnızca GNU Awk'ta kullanılabilir mi?
steeldriver

9
@ steeldriver Ben sadece GNU olduğunu düşünüyorum, evet. Ya da en azından eski versiyonlarda eksik. /pattern1/+/pattern2/==1İr ile değiştirebilirsiniz xor.
Chris

4
@JimL. Kalıp sınırlarını ( \b) kalıpların içine koyabilirsiniz , yani \bword\b.
wjandrea,

4
@vikingsteve Özellikle grep kullanmak istiyorsanız, burada birçok başka cevap var. Ancak, sadece işi yapmak isteyenler için, grep'in yaptığı her şeyi yapabilen başka araçların olduğunu, ama daha kolay olduğunu bilmek güzel.
Chris,

3
@vikingsteve Grep çözümüne olan talebin bir çeşit XY problemi olduğunu şiddetle farz ediyorum
Hagen von Eitzen

30

GNU ile grepher iki kelimeyi de geçebilir grepve sonra her iki deseni içeren çizgileri kaldırabilirsiniz.

$ cat testfile.txt
abc
def
abc def
abc 123 def
1234
5678
1234 def abc
def abc

$ grep -w -e 'abc' -e 'def' testfile.txt | grep -v -e 'abc.*def' -e 'def.*abc'
abc
def

16

İle deneyin egrep

egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

3
olarak da yazılabilirgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
Glenn jackman

8
Ayrıca, grep man sayfasından not: Direct invocation as either egrep or fgrep is deprecated- tercih edingrep -E
glenn jackman

Bu benim işletim sistemimde yok. @Glennjackman
Grump

1
@Grump gerçekten mi? Bu ne işletim sistemi? POSIX bile , grep'in daha eski olmasına ve bir süre daha desteklenmeye devam etmesine rağmen seçeneklere -fve -eseçeneklere sahip olduğundan bahsetmektedir . egrepfgrep
terdon

1
@ terdon, POSIX POSIX yardımcı programlarının yolunu belirtmiyor. Yine, standart yoktur grep(yani destekler -F, -E, -e, -fPOSIX gerektirir) içinde /usr/xpg4/bin. Yardımcı programlar /binantika olanlardır.
Stéphane Chazelas

12

İle grepuygulamalara destek düzenli ifadeler perl benzeri (gibi o pcregrepya GNU veya ast-açık grep -P), bir de bunu yapabiliyor grepile çağırma:

grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'

Yani çizgileri o maç bulmak pat1değil pat2, ya pat2ama pat1.

(?=...)ve (?!...)sırasıyla ileriye bakmak ve negatif görünüm öncesinde operatörleri vardır. Bu yüzden teknik olarak, yukarıdakiler ^takip ettiği .*pat1ve takip etmediği .*pat2veya takip ettiği pat1ve pat2tersine çevrildiği takdirde konunun ( ) başlangıcını arar .

Bu, her iki deseni içeren satırlar için daha düşüktür ve iki kez aranır. Bunun yerine daha gelişmiş perl operatörleri kullanabilirsiniz:

grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'

(?(1)yespattern|nopattern)eşleşiyor yespatternise 1st yakalama grubu (boş ()üzeri) eşleştirilmiş ve nopatternaksi. O takdirde ()kibrit, araçlarının pat1biz bakmak eşleşmedi, böylece pat2(öncesinde pozitif görünüm) ve biz bakmak değil pat2 aksi (negatif görünüm yeşil ışık).

Bununla birlikte sedyazabilirsiniz:

sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'

İlk çözümünüz grep: the -P option only supports a single pattern, en azından erişimime sahip olduğum her sistemde başarısız oluyor . Yine de ikinci çözümünüz için +1.
Chris

1
@ Chris, haklısın. Bu GNU’ya özgü bir sınırlama gibi görünüyor grep. pcregrepve ast-açık grep bu sorunu yok. Ben birden yerini ettik -eo GNU çalışmalıdır böylece, münavebe RE operatörüyle grepde şimdiki gibi.
Stéphane Chazelas

Evet, şimdi iyi çalışıyor.
Chris

3

Boolean terimleriyle, olarak yazılabilecek A xor B'yi arıyorsunuz

(A ve B değil)

veya

(B ve A değil)

Sorunuzun, eşleşen satırlar gösterildiği sürece çıktının sırası ile ilgilenmediğinizden bahsetmediği göz önüne alındığında, A xor B'nin Boolean genişlemesi, grep'te oldukça basittir:

$ cat << EOF > foo
> a b
> a
> b
> c a
> c b
> b a
> b c
> EOF
$ grep -w 'a' foo | grep -vw 'b'; grep -w 'b' foo | grep -vw 'a';
a
c a
b
c b
b c

1
Bu çalışır, ancak dosyanın sırasını karıştırır.
Sparhawk,

@Sparhawk True, "karıştırmak" zor bir kelime olmasına rağmen. ;) sırasıyla tüm 'a' eşleşmelerini sırayla, ardından sıradaki tüm 'b' eşleşmelerini listeler. OP, emrin korunmasına herhangi bir ilgi duymadı, sadece satırları göster. FAWK, bir sonraki adım olabilir sort | uniq.
Jim L.,

Adil arama; Dilimin yanlış olduğuna katılıyorum. Orijinal siparişin değişeceğini ima etmek istedim.
Sparhawk,

1
@Sparhawk ... Ve tam açıklama için gözleminizi düzenledim.
Jim L.,

-2

Aşağıdaki örnek için:

# Patterns:
#    apple
#    pear

# Example line
line="a_apple_apple_pear_a"

Bu ile tamamen yapılabilir grep -E, uniqve wc.

# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

Eğer grepPerl Düzenli ifadeler derlendi sonra yerine kadar boruya gerek son görüldüğü üzerine eşleşebilir uniq:

# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)

Sonuç çıktısı:

# Only one of the words exists if the result is < 2
((result > 0)) &&
   if (($result < 2)); then
      echo Only one word matched
   else
      echo Both words matched
   fi

Bir astar:

(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched

Deseni kodlamak istemezseniz, değişken bir eleman grubuyla birleştirilmesi bir fonksiyonla otomatikleştirilebilir.

Bu, aynı zamanda, doğal olarak Bash'de borular veya ek işlemler içermeyen bir işlev olarak da yapılabilir, ancak daha fazla ilgili olacaktır ve muhtemelen sorunuzun kapsamı dışındadır.


(1) Perl düzenli ifadelerini kullanarak birinin ne zaman cevap vereceğini merak ediyordum. Yazınızın bu bölümüne odaklandıysanız ve nasıl çalıştığını açıkladıysanız, bu iyi bir cevap olabilir. (2) Ama korkarım gerisi iyi değil. Soru, “sadece iki kelimeden birini içeren satırları göster” diyor (vurgu vurgulu). Çıkış olması gerekiyordu Eğer satırlar , o zaman giriş aynı zamanda birden fazla olması gerektiğini nedenle duruyor hatları.   Ama yaklaşım çalışır ancak sadece tek bir satırında bakarak. … (Devam ediyor)
G-Man

(Devam)… Örneğin, girdi satırları içeriyorsa Big apple\nve pear-shaped\nçıktı her iki satırı da içermelidir. Çözümünüz 2 sayısını alacaktır; Uzun versiyon “Her iki kelimeyle eşleşiyor” şeklinde rapor eder (yanlış sorunun cevabıdır) ve kısa versiyon hiç bir şey söylemezdi. (3) Bir öneri: -oburada kullanmak gerçekten kötü bir fikir, çünkü eşleşmeleri içeren satırları gizliyor, bu yüzden her iki kelimenin de aynı satırda göründüğünü göremiyorsunuz. … (Devam ediyor)
G-Man

(Devam)) (4) Alt satır: Her satırda yalnızca son olaylarla eşleşmek için Fantezi Perl düzenli ifadesini kullanmanız / uniq/ kullanmanız sort -ubu soruya gerçekten yararlı bir cevap vermez. Ancak, yapsalar bile, yine de kötü bir cevap olurdu, çünkü soruyu cevaplamaya nasıl katkıda bulunduklarını açıklamıyorsunuz . ( İyi bir açıklama örneği için Stéphane Chazelas'ın cevabına bakınız .)
G-Man

OP, "sadece iki kelimeden birini içeren satırları göstermek" istediklerini söylüyor, bu da her satırın kendi başına değerlendirilmesi gerektiği anlamına geliyor. Bunun soruyu cevaplamadığını neden düşündüğünü anlamıyorum. Lütfen başarısız olacağını düşündüğünüz bir örnek giriş sağlayın.
Zhro

Oh, bu ne anlama geldiğini? “Girdiyi bir defada bir satır okuyun ve her iki satır için bu iki veya üç komutu yerine getirin . “? (1) Kastettiğin şeyin bu olduğu acı dolu. (2) Acı verimsiz. Sizinkinden önceki dört cevap, tüm dosyanın birkaç komutla nasıl işlendiğini gösterdi (bir, iki ya da dört) ve n giriş satırı için 3 × n komutlarını çalıştırmak mı istiyorsunuz  ? Çalışsa bile, gereksiz yere pahalı uygulama için aşağı oy kazanır. (3) Tüyleri bölme riski altında, hala uygun çizgileri gösterme işini yapmaz .
G-Man 'Monica'yı Yeniden Girin'
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.