วิธีเลือกเส้นระหว่างรูปแบบเครื่องหมายสองรูปแบบซึ่งอาจเกิดขึ้นหลายครั้งด้วย awk / sed


120

ใช้awkหรือsedฉันจะเลือกเส้นที่เกิดขึ้นระหว่างรูปแบบเครื่องหมายสองแบบที่แตกต่างกันได้อย่างไร อาจมีหลายส่วนที่ทำเครื่องหมายด้วยรูปแบบเหล่านี้

ตัวอย่างเช่นสมมติว่าไฟล์มี:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

และรูปแบบเริ่มต้นคือabcและรูปแบบสิ้นสุดคือmno ดังนั้นฉันต้องการผลลัพธ์เป็น:

def1
ghi1
jkl1
def2
ghi2
jkl2

ฉันใช้ sed เพื่อจับคู่รูปแบบครั้งเดียว:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

มีวิธีใดsedหรือawk ต้องทำซ้ำ ๆ จนจบไฟล์?

คำตอบ:


189

ใช้awkกับแฟล็กเพื่อเริ่มการพิมพ์เมื่อจำเป็น:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

วิธีนี้ทำงานอย่างไร?

  • /abc/จับคู่บรรทัดที่มีข้อความนี้เช่นเดียวกับ/mno/ไม่
  • /abc/{flag=1;next}ตั้งค่าflagเมื่อabcพบข้อความ จากนั้นข้ามเส้น
  • /mno/{flag=0}ยกเลิกการตั้งค่าflagเมื่อmnoพบข้อความ
  • ขั้นสุดท้ายflagคือรูปแบบที่มีการดำเนินการเริ่มต้นซึ่งคือprint $0: ถ้าflagเท่ากับ 1 บรรทัดจะถูกพิมพ์

สำหรับคำอธิบายและตัวอย่างโดยละเอียดพร้อมกับกรณีที่แสดงหรือไม่แสดงรูปแบบโปรดดูวิธีการเลือกเส้นระหว่างสองรูปแบบ? .


31
หากคุณต้องการที่จะพิมพ์ทุกอย่างระหว่างและรวมทั้งawk '/abc/{a=1}/mno/{print;a=0}a' fileรูปแบบแล้วคุณสามารถใช้
scai

7
ใช่ @scai! หรือแม้กระทั่งawk '/abc/{a=1} a; /mno/{a=0}' file- กับเรื่องนี้วางaเงื่อนไขก่อนที่/mno/เราทำให้มันประเมินสายเป็นจริง (และพิมพ์) a=0ก่อนที่จะตั้งค่า printวิธีนี้เราสามารถหลีกเลี่ยงการเขียน
fedorqui 'SO หยุดทำร้าย'

13
@scai @fedorqui สำหรับการรวมเอาท์พุทรูปแบบคุณสามารถทำได้awk '/abc/,/mno/' file
Jotne

1
@hkasera awk '/abc/{flag=1}/mno/{flag=0}flag' fileควรทำ.
fedorqui 'SO หยุดทำร้าย'

2
@EirNym เป็นสถานการณ์แปลก ๆ ที่สามารถจัดการได้หลายวิธี: คุณต้องการพิมพ์บรรทัดใด อาจawk 'flag; /PAT1/{flag=1; next} /PAT1/{flag=0}' fileจะทำให้
fedorqui 'SO หยุดทำร้าย'

45

ใช้sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

-nตัวเลือกวิธีการที่ไม่ได้พิมพ์โดยค่าเริ่มต้น

รูปแบบค้นหาบรรทัดที่มีเพียงแค่abcถึง just mnoจากนั้นเรียกใช้การดำเนินการในไฟล์{ ... }. การดำเนินการแรกจะลบabcบรรทัด mnoบรรทัดที่สอง; และpพิมพ์บรรทัดที่เหลือ คุณสามารถผ่อนคลาย regexes ได้ตามต้องการ บรรทัดใด ๆ ที่อยู่นอกช่วงของabc.. mnoจะไม่ถูกพิมพ์ออกมา


ขอบคุณสำหรับคำตอบและคำอธิบาย! :)
dvai

@JonathanLeffler ฉันจะรู้ได้ไหมว่าจุดประสงค์ของการใช้คืออะไร-e
Kasun Siyambalapitiya

1
@KasunSiyambalapitiya: ส่วนใหญ่หมายถึงฉันชอบใช้มัน อย่างเป็นทางการระบุว่าอาร์กิวเมนต์ถัดไปคือ (ส่วนหนึ่งของ) สคริปต์ที่sedควรดำเนินการ หากคุณต้องการหรือจำเป็นต้องใช้หลายอาร์กิวเมนต์เพื่อรวมทั้งสคริปต์คุณต้องใช้-eก่อนแต่ละอาร์กิวเมนต์ดังกล่าว มิฉะนั้นจะเป็นทางเลือก (แต่ชัดเจน)
Jonathan Leffler


ดี! (ฉันชอบ sed มากกว่า awk) เมื่อใช้นิพจน์ทั่วไปที่ซับซ้อนมันจะเป็นการดีที่จะไม่ต้องทำซ้ำ เป็นไปไม่ได้ที่จะลบบรรทัดแรก / สุดท้ายของช่วง "ที่เลือก"? หรือจะใช้dกับทุกบรรทัดกับการแข่งขันนัดแรกและอีกdบรรทัดเริ่มต้นด้วยการแข่งขันที่สอง?
hans_meine

18

สิ่งนี้อาจได้ผลสำหรับคุณ (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

ลบบรรทัดทั้งหมดยกเว้นบรรทัดระหว่างบรรทัดเริ่มต้นabcและmno


!d;//dกอล์ฟ 2 ตัวดีกว่า :-) stackoverflow.com/a/31380266/895245
Ciro Santilli 郝海东冠状病六四事件法轮功

นี่มันเจ๋งมาก. การ{//!b}ป้องกันabcและไม่ให้mnoรวมอยู่ในเอาต์พุต แต่ฉันไม่สามารถหาวิธีได้ คุณช่วยอธิบายได้ไหม
Brendan

1
@Brendan คำสั่ง//!bอ่านหากบรรทัดปัจจุบันไม่ใช่บรรทัดใดบรรทัดหนึ่งที่ตรงกับช่วงให้หยุดและพิมพ์บรรทัดเหล่านั้นมิฉะนั้นบรรทัดอื่น ๆ ทั้งหมดจะถูกลบ
potong

13
sed '/^abc$/,/^mno$/!d;//d' file

ตีกอล์ฟสองตัวได้ดีกว่าppotong's {//!b};d

เครื่องหมายทับที่ว่างเปล่า//หมายถึง: "ใช้นิพจน์ทั่วไปล่าสุดที่ใช้ซ้ำ" และคำสั่งทำเช่นเดียวกับที่เข้าใจได้มากขึ้น:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

นี้น่าจะเป็น POSIX :

หาก RE ว่างเปล่า (นั่นคือไม่มีการระบุรูปแบบ) sed จะทำงานเหมือนกับว่า RE สุดท้ายที่ใช้ในคำสั่งสุดท้ายที่ใช้ (ไม่ว่าจะเป็นแอดเดรสหรือเป็นส่วนหนึ่งของคำสั่งแทนที่)


1
ฉันคิดว่าโซลูชันที่สองจะจบลงโดยไม่มีอะไรเลยเนื่องจากคำสั่งที่สองเป็นช่วงด้วย อย่างไรก็ตามความรุ่งโรจน์ในครั้งแรก
potong

@potong จริง! ฉันต้องศึกษาเพิ่มเติมว่าทำไมคนแรกถึงได้ผล ขอบคุณ!
Ciro Santilli 郝海东冠状病六四事件法轮功

7

จากลิงค์ของคำตอบก่อนหน้านี้สิ่งที่ทำเพื่อฉันซึ่งทำงานkshบน Solaris คือ:

sed '1,/firstmatch/d;/secondmatch/,$d'
  • 1,/firstmatch/d: ตั้งแต่บรรทัดที่ 1 จนถึงครั้งแรกที่คุณพบfirstmatchลบ
  • /secondmatch/,$d: ตั้งแต่ครั้งแรกsecondmatchจนถึงจุดสิ้นสุดของไฟล์ให้ลบ
  • อัฒภาคจะแยกคำสั่งสองคำสั่งซึ่งดำเนินการตามลำดับ

แค่อยากรู้ว่าทำไม range limiter ( 1,) ถึงมาก่อน/firstmatch/? ฉันเดาว่านี่อาจจะเป็นวลี'/firstmatch/1,d;/secondmatch,$d'?
Luke Davis

2
ด้วย "1, / firstmatch / d" คุณกำลังพูดว่า "จากบรรทัดที่ 1 จนถึงครั้งแรกที่คุณพบ" firstmatch "ให้ลบ" ในขณะที่ "/ secondmatch /, $ d" คุณพูด "ตั้งแต่ครั้งแรกที่เกิดขึ้นของ" secondmatch "จนถึงจุดสิ้นสุดของไฟล์ให้ลบ" อัฒภาคจะแยกคำสั่งสองคำสั่งซึ่งดำเนินการตามลำดับ
FanDeLaU

2
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

สิ่งที่ควรรู้เทียบเท่า perl เนื่องจากเป็นทางเลือกที่ดีสำหรับทั้ง awk และ sed
akhan

2

สิ่งนี้ใช้ได้กับฉัน:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

ใช้: awk -f file.awk data...

แก้ไข: โซลูชัน O_o fedorqui เป็นวิธีที่ดีกว่า / สวยกว่าของฉัน


3
ใน GNU awk if (record=1)ควรเป็นif (record==1)สองเท่า= - ดูตัวดำเนินการเปรียบเทียบ gawk
George Hawkins

2

คำตอบของ Don_crissti จากShow only text between 2 matching pattern ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

ซึ่งมีประสิทธิภาพมากขึ้นกว่าการประยุกต์ใช้ AWK ให้ดูที่นี่


ฉันไม่คิดว่าการเชื่อมโยงการเปรียบเทียบเวลาจะสมเหตุสมผลมากที่นี่เนื่องจากข้อกำหนดของคำถามแตกต่างกันมากดังนั้นการแก้ปัญหา
fedorqui 'SO หยุดทำร้าย'

2
ฉันไม่เห็นด้วยเพราะเราควรมีเกณฑ์เพื่อเปรียบเทียบคำตอบ มีเพียงไม่กี่แอปพลิเคชัน SED
LéoLéopold Hertz 준영

0

ฉันพยายามใช้awkเพื่อพิมพ์เส้นระหว่างสองรูปแบบในขณะที่pattern2 ก็ตรงกับ pattern1ด้วย และควรพิมพ์ pattern1 บรรทัดด้วย

เช่นแหล่งที่มา

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

ควรมี ouput ของ

package BBB
ddd
eee

ที่ไหน pattern1 คือpackage BBB, pattern2 package \w*คือ โปรดทราบว่าCCCไม่ใช่ค่าที่ทราบจึงไม่สามารถจับคู่ตามตัวอักษรได้

ในกรณีนี้ทั้ง @scai awk '/abc/{a=1}/mno/{print;a=0}a' fileหรือ @fedorqui ไม่ได้awk '/abc/{a=1} a; /mno/{a=0}' fileผลสำหรับฉัน

สุดท้ายก็จัดการแก้โดยawk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' fileฮ่าฮ่า

ความพยายามอีกเล็กน้อยส่งผลawk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' fileให้พิมพ์ pattern2 บรรทัดด้วยนั่นคือ

package BBB
ddd
eee
package CCC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.