ไม่ต้องการทั้งบรรทัดเพียงแค่การจับคู่จากนิพจน์ทั่วไป


15

ฉันเพียงแค่ต้องได้รับการแข่งขันจากการแสดงออกปกติ:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

ผลลัพธ์จะต้องเป็นสิ่งที่ตรงกันภายในวงเล็บ

อย่าคิดว่าฉันสามารถใช้grep ได้เพราะมันตรงกับทั้งบรรทัด

โปรดแจ้งให้เราทราบวิธีการทำเช่นนี้

คำตอบ:


12

2 สิ่ง:

  • ตามที่ระบุไว้โดย @Rory คุณต้องการ-oตัวเลือกดังนั้นจะมีการพิมพ์เฉพาะการแข่งขัน (แทนที่จะเป็นทั้งบรรทัด)
  • นอกจากนี้คุณยังกำหนด-Pตัวเลือกเพื่อใช้การแสดงผลปกติของ Perl ซึ่งรวมถึงองค์ประกอบที่มีประโยชน์เช่นLook forward (?= )และLook behind (?<= )ผู้ค้นหาชิ้นส่วน แต่ไม่ตรงกับและพิมพ์จริง

หากคุณต้องการให้จับคู่ส่วนใน parensis เท่านั้น:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

หากไฟล์มีการต่อย/(a)5667/grep จะพิมพ์ 'a' เพราะ:

  • /(ถูกค้นพบด้วย\/\(แต่เนื่องจากพวกเขาอยู่ในสภาพที่ดู (?<= )ไม่ได้รายงาน
  • aจับคู่โดย\wและถูกพิมพ์ (เนื่องจาก-o)
  • )5667/พบ b < \).+\/แต่เนื่องจากอยู่ในรูปลักษณ์ล่วงหน้า (?= )จึงไม่มีการรายงาน

18

ใช้ตัวเลือกใน-ogrep

เช่น:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar

4
ความเศร้าโศกที่ดี ... คุณมีความคิดใด ๆ ที่ฉันปล้ำกับsedbackreferences ที่จะทำเช่นนั้น?
Insyte

10
ตัวเลือก o เพื่อ grep / egrep ส่งกลับเฉพาะสิ่งที่ตรงกับนิพจน์ทั่วไปทั้งหมดไม่ใช่เฉพาะใน () ตามที่เขาขอ
Kyle Brandt

1
อย่างไรก็ตามนั่นเป็นสิ่งที่ดีมากที่จะรู้อยู่แล้ว :-)
Kyle Brandt

2
@KyleBrandt: เพื่อจับคู่เพียงส่วนเดียว (เช่น: การตัดทอน) เป็นไปได้ที่จะทำเครื่องหมายส่วนที่เหลือด้วยการมองไปข้างหน้าหรือมองไปข้างหลัง: (? <=) และ (? =)
DrYak

7
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it

4

หากคุณต้องการเฉพาะสิ่งที่อยู่ในวงเล็บคุณต้องการสิ่งที่รองรับการจับคู่ย่อย (กลุ่มที่มีชื่อหรือกลุ่มการจับหมายเลข) ฉันไม่คิดว่า grep หรือ egrep สามารถทำสิ่งนี้ได้ Perl และ sed สามารถ ตัวอย่างเช่นด้วย Perl:

หากไฟล์ที่ชื่อว่า foo มีบรรทัดอยู่ดังต่อไปนี้:

/adsdds      /

และคุณทำ:

perl -nle 'print $1 if /\/(\w).+\//' foo

จดหมาย a ถูกส่งคืน นั่นอาจไม่ใช่สิ่งที่คุณต้องการ หากคุณบอกเราว่าคุณพยายามจับคู่อะไรคุณอาจได้รับความช่วยเหลือที่ดีกว่า $ 1 คือทุกอย่างที่อยู่ในวงเล็บชุดแรก $ 2 จะเป็นชุดที่สองเป็นต้น


ฉันแค่พยายามจับคู่สิ่งที่อยู่ในวงเล็บ ดูเหมือนว่าการส่งผ่านไปยัง Perl หรือสคริปต์ PHP อาจเป็นคำตอบ
Alex L

4

เนื่องจากคุณแท็กคำถามของคุณเป็นทุบตีนอกเหนือจากเชลล์แล้วยังมีวิธีแก้ไขอื่นนอกเหนือจากgrep :

Bash มีเอ็นจิ้นนิพจน์ปกติของตัวเองตั้งแต่เวอร์ชัน 3.0 โดยใช้=~โอเปอเรเตอร์เหมือนกับ Perl

ตอนนี้ได้รับรหัสต่อไปนี้:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • โปรดทราบว่าคุณต้องเรียกใช้เป็นbashไม่ใช่shเพื่อรับส่วนขยายทั้งหมด
  • $BASH_REMATCH จะให้สตริงทั้งหมดตามที่จับคู่โดยนิพจน์ทั่วไปทั้งหมดดังนั้น <Lane>8</Lane>
  • ${BASH_REMATCH[1]} จะให้ส่วนที่ตรงกับกลุ่มที่ 1 ดังนั้นเท่านั้น 8

เรียน @DrYak ฉันหวังว่าคุณจะไม่แยกวิเคราะห์ XML ด้วย regex ที่นี่ .. :)
joonas.fi

มันยิ่งแย่กว่านี้อีก ฉันแยกวิเคราะห์ข้อมูล XML และ FASTA ที่น่ากลัว (ซึ่งทั้งสองใช้>สัญลักษณ์เพื่อจุดประสงค์ที่แตกต่างกันอย่างสิ้นเชิง) ดังที่ได้กล่าวโดยซอฟต์แวร์จัดตำแหน่ง SANSparallelเร็วขนาดใหญ่ แน่นอนว่าทั้งสองรูปแบบถูกสอดประสานเข้าด้วยกันโดยไม่มีการหลบหนีใด ๆ ดังนั้นจึงเป็นไปไม่ได้ที่จะโยนไลบรารี่ XML มาตรฐานนี้ และฉันใช้ Bash regex ณ จุดนี้ของรหัสเพราะฉันต้องการเพียงดึงข้อมูลสองสามชุดและ 2 regex ทำงานให้ฉันได้ดีกว่าการเขียน parser เฉพาะสำหรับระเบียบนี้ #LifeInBioinformatics
DrYak

กล่าวอีกอย่างคือ: มีจุดที่การแยก 1 หมายเลขเดียวทำได้ง่ายกว่าการใช้ regex rhan มากกว่าการเต้นแทงโก้ XML ทั้งหมด
DrYak

ฮะ gotcha! :)
joonas.fi

2

สมมติว่าไฟล์มี:

$ cat file
Text-here>xyz</more text

และคุณต้องการอักขระระหว่าง>และ</คุณสามารถใช้:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

ทั้งหมดจะพิมพ์สตริง "xyz"

หากคุณต้องการจับตัวเลขของบรรทัดนี้:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file


สิ่งสำคัญสำหรับฉันคือการตระหนักว่า \ d ไม่ได้ทำงานด้วยความใจเย็น มีเหตุผลที่คุณใช้ [0-9] + นั่น :)
user27432

@ user27423 มันไม่ได้ แต่ POSIX ชั้นเรียนตัวอักษร ( อ่านเจ็บปวด , การอ่านที่น่ารื่นรมย์ ) echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'ทำ: ในบางกรณี (เช่น[0-9]กับ[[:digit:]]) พวกเขาไม่ได้ช่วยให้อ่านง่ายขึ้นในบางกรณีฉันคิดว่าพวกเขาทำ (เช่น[ \t\n\r\f\v]กับ[:space:])
ซามูเอลฮาร์เมอร์

0

สิ่งนี้จะบรรลุสิ่งที่คุณร้องขอ แต่ฉันไม่คิดว่ามันเป็นสิ่งที่คุณต้องการจริงๆ ฉันวาง.*ด้านหน้าของ regex เพื่อกินอะไรก่อนการแข่งขัน แต่นั่นเป็นการดำเนินการโลภดังนั้นนี่ตรงกับ\wตัวละครสุดท้ายในสตริง

โปรดทราบว่าคุณต้องการที่จะหลบหนี parens +และ

sed 's/.*\(\w\).\+/\1/' myfile.txt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.