ด้วย grep ฉันจะจับคู่รูปแบบและกลับเข้ากับรูปแบบอื่นได้อย่างไร


11

ด้วยgrepฉันต้องการเลือกทุกบรรทัดที่ตรงกับรูปแบบและไม่ตรงกับรูปแบบอื่น ฉันต้องการที่จะใช้การร้องขอเพียงครั้งเดียวgrepเพื่อให้ฉันสามารถใช้--after-contextตัวเลือก (หรือ--before-context, หรือ--context)

-vไม่สามารถใช้งานได้ที่นี่เนื่องจากมันขัดแย้งกับรูปแบบทั้งหมดที่ฉันส่งผ่านไปยังการgrepใช้-eตัวเลือก

ตัวอย่าง

ฉันต้องการค้นหาการจับคู่บรรทัดneedleละเว้นการจับคู่บรรทัดignore meด้วยบริบทต่อไปนี้หนึ่งบรรทัด

นี่คือไฟล์อินพุตของฉัน:

one needle ignore me
two
three
four needle
five

ผลลัพธ์ที่ฉันต้องการคือ:

four needle
five

อย่างที่คุณเห็นโซลูชั่นไร้เดียงสานี้ไม่ทำงาน:

$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five

คำตอบ:


10

ถ้าคุณมี GNU grep คุณสามารถใช้นิพจน์ปกติ Perlซึ่งมีโครงสร้างการปฏิเสธ

grep -A1 -P '^(?!.*ignore me).*needle'

หากคุณไม่ได้มี GNU grep คุณสามารถเลียนแบบของมันก่อน / หลังตัวเลือกในบริบท awk

awk -v after=3 -v before=2 '
/needle/ && !/ignore me/ {
    for (i in h) {
        print h[i];
        delete h[i];
    }
    until = NR + after;
}
{
    if (NR <= until) print $0; else h[NR] = $0;
    delete h[NR-before];
}
END {exit !until}
'

8

ดูเหมือนว่าคุณจะใช้ GNU grepด้วย GNU grep คุณสามารถส่งผ่านการ--perl-regexตั้งค่าสถานะเพื่อเปิดใช้งาน PCRE จากนั้นจัดหาการยืนยันเชิงลบ lookahead ตัวอย่างด้านล่าง

grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five

สิ่งสำคัญของการบันทึกที่นี่(?:(?!STRING).)*คือการSTRINGเป็น[^CHAR]*ไปCHAR


@ 1_CR ... ท่าน .. มันยอดเยี่ยม .. : P มีคำตอบบางอย่างให้ack
Rahul Patil

@RahulPatil :-) ใช่ grep GNU เป็นสิ่งที่ดี
iruvar

นั่นไม่ใช่สิ่งที่ฉันต้องการ ฉันต้องการให้ทำงานไม่ว่า "ละเว้นฉัน" ก่อนหรือหลัง "เข็ม"
Flimm

@RahulPatil ขอบคุณฉันซ่อมมันในเวอร์ชั่นล่าสุด
iruvar

มีประโยชน์มาก. โดยเฉพาะอย่างยิ่งในกรณีของ grep ที่มีบริบทที่คุณต้องการแยกบรรทัดที่ใกล้เคียง แต่ไม่มีรูปแบบบางส่วน ใกล้กับคำถามต้นฉบับ แต่ไม่เหมือนกัน
gaoithe

2

ฉันอยากจะแนะนำให้ใช้ awk แทนเพราะมันจัดการกับ multi-line IO ได้ดีขึ้น อย่างใดอย่างหนึ่ง1)ไพพ์ผลลัพธ์ไปยัง GNU awk ด้วย--\nเป็นตัวคั่นเร็กคอร์ดหรือ2)ทำการจับคู่ทั้งหมดใน awk

ตัวเลือกที่ 1

<file grep -A1 needle | awk '!/ignore me/' RS='--\n' ORS='--\n'

เอาท์พุท:

four needle                                                                                  
five
--

หมายเหตุตัวเลือกนี้ค้นหาระเบียนทั้งหมดเพื่อignore meตั้งค่าFS=1และจับคู่กับ$1เปรียบเทียบกับบรรทัดแรกเท่านั้น

ตัวเลือก 2

<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1

มีignore meไฟล์จำนวนมากไหม awk ไม่ทำงาน
Rahul Patil

@RahulPatil: คุณสามารถใช้ถ้อยคำใหม่หรือเพิ่มรายละเอียดให้กับคำถามของคุณได้ไหม? ฉันไม่เข้าใจสิ่งที่คุณถาม
Thor

@ พวกเขาทดสอบตัวอย่างของคุณด้วยไฟล์อินพุตนี้paste.ubuntu.com/6252860
Rahul Patil

@RahulPatil: ฉันเห็นสิ่งที่คุณหมายถึงตอนนี้ตัวเลือกที่ 1ถือว่า--\nตัวคั่นอยู่ระหว่างกลุ่มที่จับคู่กันซึ่งไม่ได้อยู่ตรงนั้นถ้ากลุ่มอยู่ติดกัน วิธีจัดการกลุ่มที่อยู่ติดกันเป็นงานเฉพาะดังนั้นจึงไม่จำเป็นต้องผิด ตัวเลือกที่ 2ไม่ขึ้นอยู่กับตัวคั่นและไม่ได้รับผลกระทบ
Thor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.