วิธีใช้ grep เพื่อค้นหาบรรทัดที่มีหนึ่งในสองคำ แต่ไม่ใช่ทั้งสองอย่าง?


11

ฉันต้องการค้นหาบรรทัดด้วย 'word1' XOR 'word2' ในไฟล์ข้อความ ดังนั้นควรแสดงผลลัพธ์ด้วย word1, word2 แต่ไม่ใช่บรรทัดที่มีทั้งสองคำนี้ ฉันต้องการใช้ XOR แต่ฉันไม่รู้วิธีเขียนในบรรทัดคำสั่ง linux

ฉันเหนื่อย:

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

และอื่น ๆ อีกมากมาย แต่ไม่สามารถทำได้

คำตอบ:


6

grep 'word1\|word2' text.txtค้นหาบรรทัดที่มีหรือword1 word2ซึ่งรวมถึงบรรทัดที่มีทั้งคู่

grep word1 text.txt | grep word2ค้นหาบรรทัดที่มีและword1 word2คำสองคำสามารถทับซ้อนกัน (เช่นfoobarมีfooและob) อีกวิธีในการค้นหาบรรทัดที่มีทั้งสองคำ แต่ใช้วิธีที่ไม่ทับซ้อนกันคือค้นหาโดยเรียงตามลำดับ:grep 'word1.*word2\|word2.*word1' text.txt

grep word1 text.txt | grep -v word2ค้นหาบรรทัดที่มีแต่ไม่word1 ตัวเลือกบอก grep เพื่อให้เส้นไม่ตรงและเส้นลบจับคู่แทนที่จะตรงข้าม สิ่งนี้จะให้ผลลัพธ์ครึ่งเดียวกับที่คุณต้องการ ด้วยการเพิ่มการค้นหาแบบสมมาตรคุณจะได้บรรทัดทั้งหมดที่มีหนึ่งในคำทั้งหมดword2-v

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

หรือคุณสามารถเริ่มจากบรรทัดที่มีคำใดคำหนึ่งและลบบรรทัดที่มีทั้งคำ ด้วยการสร้างบล็อคด้านบนนี่เป็นเรื่องง่ายถ้าคำไม่ทับซ้อนกัน

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

ขอบคุณนี่คือสิ่งที่ฉันกำลังมองหา คำตอบอื่น ๆ ก็น่าสนใจมาก ๆ ขอบคุณทุกคนที่ให้ความช่วยเหลือ
Lukali

17

ด้วย GNU awk:

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

หรือพกพา:

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

ด้วยการgrepสนับสนุน-P(PCRE):

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

ด้วยsed:

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

หากคุณต้องการพิจารณาทั้งคำเท่านั้น (ที่ไม่มีfooหรือbarในfoobarหรือbarbarตัวอย่าง) คุณจะต้องตัดสินใจว่าคำเหล่านั้นจะคั่นด้วย หากเป็นตัวละครอื่นที่ไม่ใช่ตัวอักษรตัวเลขและขีดล่างเช่นเดียวกับ-wตัวเลือกในgrepการใช้งานหลายอย่างคุณควรเปลี่ยนเป็น:

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

สำหรับการsedที่จะกลายเป็นบิตซับซ้อนจนกว่าคุณจะมีsedการดำเนินการเช่น GNU sed ที่สนับสนุน\</ \>เป็นขอบเขตของคำเช่น GNU awkไม่


6
สเตฟานโปรดเขียนหนังสือเกี่ยวกับการเขียนสคริปต์เชลล์!
pfnuesel

ขอโทษฉันเพิ่งเริ่มบรรทัดคำสั่งไม่กี่สัปดาห์ที่ผ่านมา ฉันจะบังคับให้ค้นหาเฉพาะคำได้อย่างไร ฉันพยายาม -Pw และ -wP แต่นี่ให้ผลลัพธ์ผิดฉัน ฉันพยายามใช้ '' ระหว่าง * word1 / * word2 และประมาณ word1 / word2
Lukali

@Lukali ดูการแก้ไข
Stéphane Chazelas

2

วิธีทุบตี:

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

เพื่อทดสอบ:

$ ./script {foo,bar}\ {foo,bar} neither
foo foo
bar bar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.