Bagaimana cara menggunakan grep untuk mencari baris dengan satu dari dua kata tetapi tidak keduanya?


11

Saya ingin mencari baris dengan 'word1' XOR 'word2' dalam file teks. Jadi seharusnya menghasilkan baris dengan kata1, kata2 tetapi bukan baris dengan kedua kata ini. Saya ingin menggunakan XOR tetapi saya tidak tahu bagaimana menulisnya di baris perintah linux.

Saya mencoba:

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

dan masih banyak lagi, tetapi tidak berhasil.

Jawaban:


6

grep 'word1\|word2' text.txtmencari baris yang mengandung word1atau word2. Ini termasuk baris yang mengandung keduanya.

grep word1 text.txt | grep word2mencari baris yang mengandung word1dan word2. Kedua kata tersebut dapat tumpang tindih (misalnya foobarberisi foodan ob). Cara lain untuk mencari baris yang mengandung kedua kata, tetapi hanya dengan cara yang tidak tumpang tindih, adalah dengan mencari mereka dalam urutan apa pun:grep 'word1.*word2\|word2.*word1' text.txt

grep word1 text.txt | grep -v word2mencari baris yang mengandung word1tetapi tidak word2. The -vpilihan memberitahu grep untuk menjaga garis non-pencocokan dan garis menghapus pencocokan, bukan sebaliknya. Ini memberi Anda setengah hasil yang Anda inginkan. Dengan menambahkan pencarian simetris, Anda mendapatkan semua baris yang mengandung tepat salah satu kata.

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

Atau, Anda bisa mulai dari baris yang mengandung kata mana pun, dan menghapus garis yang mengandung kedua kata tersebut. Mengingat blok bangunan di atas, ini mudah jika kata-katanya tidak tumpang tindih.

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

Terima kasih, inilah tepatnya yang saya cari. Jawaban yang lain juga sangat menarik sehingga mereka tidak bisa melihatnya. Terima kasih semuanya telah berkontribusi.
Lukali

17

Dengan GNU awk:

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

Atau mudah dibawa:

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

Dengan grepdukungan untuk -P(PCRE):

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

Dengan sed:

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

Jika Anda ingin mempertimbangkan seluruh kata saja (bahwa tidak ada fooatau tidak ada bardalam foobaratau barbarmisalnya), Anda harus memutuskan bagaimana kata-kata itu dibatasi. Jika dengan karakter apa pun selain huruf, angka, dan garis bawah seperti -wopsi banyak grepimplementasi, maka Anda akan mengubahnya menjadi:

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

Untuk seditu menjadi sedikit rumit kecuali jika Anda memiliki sedimplementasi seperti GNU sed yang mendukung \</ \>sebagai batas kata seperti GNU awk.


6
Stephane, tolong tulis buku tentang shell scripting!
pfnuesel

Maaf saya baru memulai baris perintah beberapa minggu yang lalu. Bagaimana saya memaksanya untuk hanya mencari kata-kata? Saya mencoba -W dan WW tetapi ini memberi saya output yang salah. Saya juga mencoba menggunakan '' antara * word1 / * word2 dan sekitar word1 / word2.
Lukali

@Lukali, lihat edit.
Stéphane Chazelas

2

Solusi bash:

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

Untuk mengujinya:

$ ./script {foo,bar}\ {foo,bar} neither
foo foo
bar bar
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.