ฉันพยายามใช้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/==1ir 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หรือแต่ไม่pat2pat1
(?=...)และ(?!...)มีการมองไปข้างหน้าตามลำดับและมองไปข้างหน้าเชิงลบผู้ประกอบการ ดังนั้นในทางเทคนิคแล้วข้างต้นจะมองหาจุดเริ่มต้นของเรื่อง ( ^) ที่จัดให้ตามด้วย.*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 สำหรับโซลูชันที่สองของคุณ
grepGNU 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, และuniqwc
# 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) จะเกิดอะไรขึ้นหากคำใดคำหนึ่ง / รูปแบบปรากฏมากกว่าหนึ่งครั้งในหนึ่งบรรทัด (และอีกอันหนึ่งไม่ปรากฏ) นั่นเท่ากับคำที่ปรากฏครั้งเดียวหรือนับเป็นหลายครั้งหรือไม่