Grep'i iki kelimeden birini içeren bir satır aramak için nasıl kullanabilirim?


11

Bir metin dosyasında 'word1' XOR 'word2' olan satırları aramak istiyorum. Bu nedenle word1, word2 içeren satırlar çıkarmalı, ancak her iki sözcüğün de bulunduğu satırlar çıkmamalıdır. XOR kullanmak istedim ama linux komut satırına nasıl yazacağımı bilmiyorum.

Denedim:

grep 'word1\|word2' text.txt
grep word1 word2 text.txt
grep word1 text.txt | grep word2
grep 'word1\^word2' text.txt

ve çok daha fazlası, ama başarılı olamadı.

Yanıtlar:


6

grep 'word1\|word2' text.txtword1veya içeren satırları arar word2. Bu, her ikisini de içeren satırları içerir.

grep word1 text.txt | grep word2word1ve içeren satırları arar word2. İki kelime (örneğin üst üste foobariçerir foove ob). Her iki kelimeyi içeren satırları aramanın başka bir yolu, ancak üst üste binmeyen bir şekilde, bunları her iki sırayla aramaktır:grep 'word1.*word2\|word2.*word1' text.txt

grep word1 text.txt | grep -v word2içeren word1ancak içermeyen satırları arar word2. Bu -vseçenek grep'e eşleşmeyen çizgileri tutmasını ve tersine eşleşen çizgileri kaldırmasını söyler. Bu size istediğiniz sonuçların yarısını verir. Simetrik aramayı ekleyerek, tam olarak kelimelerden birini içeren tüm satırları elde edersiniz.

grep word1 text.txt | grep -v word2
grep word2 text.txt | grep -v word1

Alternatif olarak, her iki sözcüğü içeren satırlardan başlayabilir ve her iki sözcüğü içeren satırları kaldırabilirsiniz. Yukarıdaki yapı taşları göz önüne alındığında, kelimeler üst üste binmiyorsa bu kolaydır.

grep 'word1\|word2' text.txt | grep -v 'word1.*word2\|word2.*word1'

Teşekkürler tam da aradığım şey bu. Diğer cevaplar da çok ilginç, bu yüzden onlara bakıyorum. Katkınız için herkese teşekkürler.
Lukali

17

GNU ile awk:

$ printf '%s\n' {foo,bar}{bar,foo} neither | gawk 'xor(/foo/,/bar/)'
foofoo
barbar

Veya portatif olarak:

awk '((/foo/) + (/bar/)) % 2'

Bir ile grepiçin birlikte destek -P(PCRE):

grep -P '^((?=.*foo)(?!.*bar)|(?=.*bar)(?!.*foo))'

İle sed:

sed '
  /foo/{
    /bar/d
    b
  }
  /bar/!d'

Yalnızca tam kelimeleri dikkate almak istiyorsanız ( foone bariçinde ne de içinde foobarveya ne olduğunu yoktur barbar), bu kelimelerin nasıl sınırlandırıldığına karar vermeniz gerekir. Harfler, rakamlar ve alt çizgi -wdışında birçok grepuygulamanın seçeneği gibi bir karaktere sahipse, bunları şu şekilde değiştirebilirsiniz:

gawk 'xor(/\<foo\>/,/\<bar\>/)'
awk '((/(^|[^[:alnum:]_)foo([^[:alnum:]_]|$)/) + \
      (/(^|[^[:alnum:]_)bar([^[:alnum:]_]|$)/)) % 2'
grep -P '^((?=.*\bfoo\b)(?!.*\bbar\b)|(?=.*\bbar\b)(?!.*\bfoo\b))'

İçin sedsize bir yoksa biraz karışık hale sedGNU gibi uygulanmasını sed desteklediği bu \</ \>GNU gibi kelime sınırları olarak awkyapar.


6
Stephane, lütfen kabuk senaryosu hakkında bir kitap yaz!
pfnuesel

Üzgünüm sadece birkaç hafta önce komut satırına başladım. Bunu yalnızca kelimeleri aramaya nasıl zorlarım? -Pw ve -wP denedim ama bu bana yanlış çıktı verdi. Ayrıca '' word1 / * word2 ile word1 / word2 arasında '' kullanmaya çalıştım.
Lukali

@Lukali, bakınız düzenleme.
Stéphane Chazelas

2

Bir bash çözümü:

#!/bin/bash 
while (( $# )); do
    a=0 ; [[ $1 =~ foo ]] && a=1 
    b=0 ; [[ $1 =~ bar ]] && b=1
    (( a ^ b )) && echo "$1"
    shift
done

Test etmek için:

$ ./script {foo,bar}\ {foo,bar} neither
foo foo
bar bar
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.