ฉันพยายามใช้grep
เพื่อแสดงเฉพาะบรรทัดที่มีคำใดคำหนึ่งในสองคำหากมีเพียงคำเดียวที่ปรากฏในบรรทัด แต่ไม่ใช่หากอยู่ในบรรทัดเดียวกัน
จนถึงตอนนี้ฉันพยายามแล้ว grep pattern1 | grep pattern2 | ...
แต่ไม่ได้ผลลัพธ์ที่ฉันคาดไว้
ฉันพยายามใช้grep
เพื่อแสดงเฉพาะบรรทัดที่มีคำใดคำหนึ่งในสองคำหากมีเพียงคำเดียวที่ปรากฏในบรรทัด แต่ไม่ใช่หากอยู่ในบรรทัดเดียวกัน
จนถึงตอนนี้ฉันพยายามแล้ว grep pattern1 | grep pattern2 | ...
แต่ไม่ได้ผลลัพธ์ที่ฉันคาดไว้
คำตอบ:
เครื่องมืออื่นที่ไม่ใช่grep
วิธีที่จะไป
ยกตัวอย่างเช่นการใช้ Perl คำสั่งจะเป็น:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
รันคำสั่งที่กำหนดเหนือแต่ละบรรทัดของ stdin ซึ่งในกรณีนี้พิมพ์บรรทัดหากตรง/pattern1/ xor /pattern2/
หรือในคำอื่น ๆ ที่ตรงกับหนึ่งรูปแบบ แต่ไม่อื่น ๆ (พิเศษหรือ)
สิ่งนี้ใช้ได้กับรูปแบบตามลำดับใด ๆ และควรมีประสิทธิภาพที่ดีกว่าการเรียกใช้หลายรายการgrep
และพิมพ์น้อยลงเช่นกัน
หรือสั้นกว่าด้วย awk:
awk 'xor(/pattern1/,/pattern2/)'
หรือสำหรับรุ่น awk ที่ไม่มีxor
:
awk '/pattern1/+/pattern2/==1`
xor
มีใน GNU Awk เท่านั้นหรือไม่
/pattern1/+/pattern2/==1
ir xor
จะหายไป
\b
) \bword\b
ในรูปแบบของตัวเองคือ
ด้วย GNU grep
คุณสามารถส่งผ่านทั้งสองคำไปgrep
แล้วลบบรรทัดที่มีทั้งลวดลาย
$ 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
ลองด้วย egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
Direct invocation as either egrep or fgrep is deprecated
- prefergrep -E
grep
(ที่สนับสนุน-F
, -E
, -e
, -f
เป็น POSIX ต้อง) /usr/xpg4/bin
อยู่ใน สาธารณูปโภคใน/bin
นั้นเป็นวัตถุโบราณ
ด้วยgrep
การใช้งานที่รองรับการแสดงออกปกติแบบ perl (เช่นpcregrep
หรือ GNU หรือ ast-open grep -P
) คุณสามารถทำได้ในgrep
การเรียกใช้ครั้งเดียวด้วย:
grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
นั่นคือการหาเส้นที่การแข่งขันpat1
แต่ไม่pat2
หรือแต่ไม่pat2
pat1
(?=...)
และ(?!...)
มีการมองไปข้างหน้าตามลำดับและมองไปข้างหน้าเชิงลบผู้ประกอบการ ดังนั้นในทางเทคนิคแล้วข้างต้นจะมองหาจุดเริ่มต้นของเรื่อง ( ^
) ที่จัดให้ตามด้วย.*pat1
และไม่ตามด้วย.*pat2
หรือเหมือนกับpat1
และpat2
ย้อนกลับ
นั่นเป็นสิ่งที่ไม่ดีสำหรับเส้นที่มีลวดลายทั้งสองแบบ คุณสามารถใช้ตัวดำเนินการ Perl ขั้นสูงแทนเช่น:
grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'
(?(1)yespattern|nopattern)
จับคู่กับyespattern
ถ้ากลุ่มการจับภาพ1
เซนต์ (ว่างเปล่า()
ด้านบน) จับคู่และnopattern
อย่างอื่น หากการ()
จับคู่นั้นหมายความว่าpat1
ไม่ตรงกันดังนั้นเราจึงมองหาpat2
(มองในแง่ดีล่วงหน้า) และมองหาไม่ pat2
อย่างอื่น (มองในแง่ลบ)
ด้วยsed
คุณสามารถเขียนมัน:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
grep: the -P option only supports a single pattern
อย่างน้อยในทุกระบบที่ฉันเข้าถึง +1 สำหรับโซลูชันที่สองของคุณ
grep
GNU pcregrep
และ grep ที่เปิดอยู่ไม่มีปัญหานั้น ฉันได้แทนที่ตัวคูณ-e
ด้วยตัวดำเนินการ RE สำรองดังนั้นมันควรทำงานกับ GNU grep
เช่นกันในตอนนี้
ในแง่บูลีนคุณกำลังมองหา A xor B ซึ่งสามารถเขียนเป็น
(A และไม่ใช่ B)
หรือ
(B และไม่ใช่ A)
เนื่องจากคำถามของคุณไม่ได้กล่าวถึงว่าคุณมีความกังวลกับลำดับของผลลัพธ์ตราบใดที่มีการแสดงบรรทัดที่ตรงกันการขยายบูลีนของ A xor B นั้นค่อนข้างง่ายใน grep:
$ 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
sort | uniq
ได้
สำหรับตัวอย่างต่อไปนี้:
# Patterns:
# apple
# pear
# Example line
line="a_apple_apple_pear_a"
ซึ่งสามารถทำได้อย่างหมดจดด้วยgrep -E
, และuniq
wc
# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)
หากgrep
มีการรวบรวมด้วยการแสดงออกปกติ Perl แล้วคุณสามารถจับคู่ในการเกิดขึ้นครั้งสุดท้ายแทนที่จะต้องไปป์uniq
:
# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)
ส่งออกผลลัพธ์:
# 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
หนึ่งซับ:
(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched
หากคุณไม่ต้องการเขียนโค้ดรูปแบบที่ยากการประกอบมันด้วยชุดองค์ประกอบที่หลากหลายสามารถเป็นไปโดยอัตโนมัติด้วยฟังก์ชั่น
สิ่งนี้สามารถทำได้โดยกำเนิดใน Bash เป็นฟังก์ชันโดยไม่มีไพพ์หรือกระบวนการเพิ่มเติม แต่จะเกี่ยวข้องมากกว่าและอาจอยู่นอกขอบเขตของคำถามของคุณ
Big apple\n
และpear-shaped\n
ดังนั้นเอาต์พุตควรมีทั้งสองบรรทัด โซลูชันของคุณจะได้รับการนับ 2; รุ่นยาวจะรายงาน“ ทั้งสองคำที่ตรงกัน” (ซึ่งเป็นคำตอบสำหรับคำถามที่ผิด) และรุ่นสั้นจะไม่พูดอะไรเลย (3) ข้อเสนอแนะ: การใช้-o
ที่นี่เป็นความคิดที่ไม่ดีจริงๆเพราะมันจะซ่อนบรรทัดที่มีการจับคู่ไว้ดังนั้นคุณจึงไม่สามารถมองเห็นเมื่อทั้งสองคำปรากฏในบรรทัดเดียวกัน … (ต่อ)
uniq
/ sort -u
และการแสดงออกปกติของ Perl แฟนซีเพื่อให้ตรงกับการเกิดขึ้นครั้งสุดท้ายในแต่ละบรรทัดไม่ได้เพิ่มคำตอบที่เป็นประโยชน์สำหรับคำถามนี้ แต่แม้ว่าพวกเขาจะทำเช่นนั้นก็ยังคงเป็นคำตอบที่ไม่ดีเพราะคุณไม่ได้อธิบายว่าพวกเขามีส่วนช่วยตอบคำถามอย่างไร (ดูคำตอบของStéphane Chazelasเพื่อเป็นตัวอย่างของคำอธิบายที่ดี)
[a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+
อย่างไร (2) จะเกิดอะไรขึ้นหากคำใดคำหนึ่ง / รูปแบบปรากฏมากกว่าหนึ่งครั้งในหนึ่งบรรทัด (และอีกอันหนึ่งไม่ปรากฏ) นั่นเท่ากับคำที่ปรากฏครั้งเดียวหรือนับเป็นหลายครั้งหรือไม่