sed: พิมพ์เฉพาะกลุ่มที่ตรงกัน


133

ฉันต้องการจับตัวเลขสองตัวสุดท้าย (หนึ่ง int, หนึ่ง float; ตามด้วยช่องว่างที่เลือกได้) และพิมพ์เฉพาะตัวเลขนั้น

ตัวอย่าง:

foo bar <foo> bla 1 2 3.4

ควรพิมพ์:

2 3.4

จนถึงตอนนี้ฉันมีสิ่งต่อไปนี้:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

จะให้ฉัน

foo bar <foo> bla 1 replacement

อย่างไรก็ตามหากฉันพยายามแทนที่ด้วยกลุ่ม 1 จะมีการพิมพ์ทั้งบรรทัด

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/\1/p' 

ฉันจะพิมพ์เฉพาะส่วนของบรรทัดที่ตรงกับนิพจน์ทั่วไปในกลุ่มได้อย่างไร

คำตอบ:


138

จับคู่ทั้งบรรทัดดังนั้นให้เพิ่ม.*ที่จุดเริ่มต้นของ regex ของคุณ ทำให้ทั้งบรรทัดถูกแทนที่ด้วยเนื้อหาของกลุ่ม

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4

38
ฉันต้องเพิ่ม-rหรือตัวเลือก "--regexp-Extended" มิฉะนั้นฉันได้รับinvalid reference \1 on ข้อผิดพลาด RHS ของคำสั่ง s
Daniel Sokolowski

15
@DanielSokolowski ผมคิดว่าคุณจะได้รับข้อผิดพลาดว่าถ้าคุณใช้(และ)แทนและ\( \)
Daniel Darabos

3
อย่าลืมเพิ่ม.*ที่ส่วนท้ายของ regexp หากสตริงที่คุณต้องการแยกไม่ได้อยู่ท้ายบรรทัดเสมอไป
Teemu Leisti

3
สิ่งนี้จะไม่ได้ผลสำหรับฉันเพราะ.*เป็นคนโลภและ sed ไม่มีความโลภ.*?
sondra.kinsey

@DanielDarabos เพียงแค่พูดถึงสิ่งนั้น(และ)จะไม่เพิ่มข้อผิดพลาดใน ubuntu 16.04 ดังนั้นฉันคิดว่าความคิดเห็นนี้ล้าสมัย
Li haonan

72

grepเป็นเครื่องมือที่เหมาะสมสำหรับการแยกไฟล์

โดยใช้ตัวอย่างของคุณและ regex ของคุณ:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4

12
เหมาะสำหรับทั้งกลุ่มแม้ว่าsed เป็นสิ่งจำเป็นสำหรับแต่ละกลุ่ม
jozxyqk

grep -o ไม่ได้พอร์ตบนระบบที่รัน msysgit แต่ sed ทำ
cchamberlain

ดูคำถามที่เชื่อมโยงโดย @jozxyqk สำหรับคำตอบที่ใช้การมองไปข้างหน้าและมองข้างหลังเพื่อแก้ปัญหานี้ด้วย grep
Joachim Breitner

คุณสามารถแยกกลุ่มออกจากรูปแบบด้วยการgrep -oโทรไปป์ stackoverflow.com/a/58314379/117471
Bruno Bronosky

12

และสำหรับตัวเลือกอื่นฉันจะไปกับ awk!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

การดำเนินการนี้จะแบ่งอินพุต (ฉันใช้ STDIN ที่นี่ แต่อินพุตของคุณอาจเป็นไฟล์ได้อย่างง่ายดาย) ในช่องว่างจากนั้นพิมพ์ฟิลด์สุดท้าย - แต่ - หนึ่งฟิลด์จากนั้นฟิลด์สุดท้าย $NFตัวแปรถือจำนวนของเขตข้อมูลพบว่าหลังจากระเบิดในช่องว่าง

ข้อดีของสิ่งนี้คือไม่สำคัญว่าสิ่งที่เกิดขึ้นก่อนหน้าสองฟิลด์สุดท้ายจะเปลี่ยนไปหรือไม่ตราบใดที่คุณต้องการเพียงสองฟิลด์สุดท้ายเท่านั้นที่จะยังคงใช้งานได้


3

คำสั่งตัดถูกออกแบบมาสำหรับสถานการณ์นี้ มันจะ "ตัด" บนตัวคั่นใด ๆ จากนั้นคุณสามารถระบุได้ว่าควรจะส่งออกชิ้นส่วนใด

ตัวอย่างเช่น: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

จะส่งผลให้ผลลัพธ์ของ: 2 3.4

-d ตั้งค่าตัวคั่น

-f เลือกช่วงของ 'fields' ที่จะส่งออกในกรณีนี้เป็นกลุ่มที่ 6 ถึง 7 ของสตริงเดิม 6,7นอกจากนี้คุณยังสามารถระบุช่วงเป็นรายการเช่น


หากต้องการพิมพ์เฉพาะบางคอลัมน์ให้ไปที่awk '{ print $2" "$6 }'
nurettin

@nurettin ฉันคิดว่าความคิดเห็นของคุณอาจมีความหมายสำหรับหนึ่งในคำตอบ awk
carlin.scott

ฉันพยายามตัดเมื่อฉันเยี่ยมชมหน้านี้และตระหนักว่ามันเป็นข้อ จำกัด และตัดสินใจที่จะเขียนเวอร์ชันทั่วไปใน awk แทนเพื่อเป็นความคิดเห็นเพื่อปรับปรุงคุณภาพของโพสต์นี้
nurettin

1
ใช่ฉันคิดว่าเป็นคำตอบอื่นที่เกี่ยวข้องกับ awk คำสั่งตัดเพื่อทำสิ่งที่คุณเขียนคือ:cut -d " " -f 2,6
carlin.scott

อาฉันไม่รู้ฉันคิดว่าคุณสามารถกำหนดช่วงได้เท่านั้น ขอบคุณสำหรับสิ่งนั้น
nurettin

2

ผมเห็นด้วยกับ@ Kentgrep -oที่ว่านี้มีความเหมาะสมดีสำหรับ หากคุณต้องการแยกกลุ่มภายในรูปแบบคุณสามารถทำได้ด้วย grep ที่ 2

# To extract \1 from /xx([0-9]+)yy/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
123
4

# To extract \1 from /a([0-9]+)b/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
678
9
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.