เมื่อ regexp มีกลุ่มอาจมีมากกว่าหนึ่งวิธีในการจับคู่สตริงกับมัน: regexps กับกลุ่มไม่ชัดเจน ยกตัวอย่างเช่นพิจารณา regexp และสตริง^.*\([0-9][0-9]*\)$
a12
มีความเป็นไปได้สองอย่าง:
- ตรง
a
กับ.*
และ2
ต่อต้าน[0-9]*
; จะถูกจับคู่โดย1
[0-9]
- จับคู่
a1
กับ.*
และสตริงที่ว่างเปล่ากับ[0-9]*
; จะถูกจับคู่โดย2
[0-9]
Sed เช่นเดียวกับเครื่องมือ regexp อื่น ๆ ทั้งหมดออกมีใช้กฎการจับคู่ที่ยาวที่สุดที่เร็วที่สุด: มันพยายามจับคู่ส่วนที่มีความยาวตัวแปรแรกกับสตริงที่ยาวที่สุดเท่าที่จะทำได้ หากพบวิธีจับคู่ส่วนที่เหลือของสตริงกับส่วนที่เหลือของ regexp ให้ปรับ มิฉะนั้น sed จะพยายามจับคู่ที่ยาวที่สุดถัดไปสำหรับส่วนที่มีความยาวผันแปรแรกและลองอีกครั้ง
นี่คือการแข่งขันกับสตริงที่ยาวที่สุดเป็นครั้งแรกa1
กับดังนั้นกลุ่มเดียวที่ตรงกับ.*
2
หากคุณต้องการให้กลุ่มเริ่มต้นก่อนหน้านี้เครื่องยนต์ regexp บางตัวช่วยให้คุณ.*
โลภน้อยลง แต่ยังไม่มีคุณสมบัติดังกล่าว ดังนั้นคุณต้องลบความกำกวมด้วยสมอเพิ่มเติมบางอย่าง ระบุว่าผู้นำ.*
ไม่สามารถลงท้ายด้วยตัวเลขดังนั้นตัวเลขแรกของกลุ่มคือการจับคู่ที่เป็นไปได้ครั้งแรก
หากกลุ่มของตัวเลขไม่สามารถอยู่ที่จุดเริ่มต้นของบรรทัด:
sed -n 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p'
หากกลุ่มของตัวเลขสามารถอยู่ที่จุดเริ่มต้นของบรรทัดและ sed ของคุณสนับสนุน\?
ผู้ประกอบการสำหรับชิ้นส่วนเพิ่มเติม:
sed -n 's/^\(.*[^0-9]\)\?\([0-9][0-9]*\).*/\1/p'
หากกลุ่มของตัวเลขสามารถอยู่ที่จุดเริ่มต้นของบรรทัดการผสานกับโครงสร้าง regexp มาตรฐาน:
sed -n -e 's/^.*[^0-9]\([0-9][0-9]*\).*/\1/p' -e t -e 's/^\([0-9][0-9]*\).*/\1/p'
โดยวิธีการที่มันเป็นเรื่องที่กฎแข่งขันที่เก่าแก่ที่สุดที่ยาวที่สุดที่ทำให้ตรงกับหลักหลังคนแรกมากกว่าต่อมา[0-9]*
.*
.*
โปรดทราบว่าถ้ามีหลายลำดับของตัวเลขในบรรทัดโปรแกรมของคุณมักจะแยกลำดับสุดท้ายของตัวเลขอีกครั้งเพราะกฎการแข่งขันที่ยาวที่สุดเร็วที่สุดเท่าที่นำไปใช้กับการเริ่มต้น หากคุณต้องการแยกลำดับของตัวเลขแรกคุณต้องระบุว่าสิ่งที่มาก่อนคือลำดับของตัวเลขที่ไม่ใช่
sed -n 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/p'
โดยทั่วไปหากต้องการแยกคู่แรกของ regexp คุณจำเป็นต้องคำนวณการปฏิเสธของ regexp นั้น ในขณะที่สิ่งนี้เป็นไปได้ในทางทฤษฎีขนาดของการปฏิเสธจะเพิ่มขึ้นแบบทวีคูณเมื่อเทียบกับขนาดของ regexp ที่คุณกำลังปฏิเสธดังนั้นนี่จึงเป็นสิ่งที่ไม่จริง
ลองพิจารณาตัวอย่างอื่นของคุณ:
sed -n 's/.*\(CONFIG_[a-zA-Z0-9_]*\).*/\1/p'
ตัวอย่างนี้จริงแสดงปัญหาเดียวกัน แต่คุณไม่เห็นในอินพุตปกติ ถ้าคุณกินมันhello CONFIG_FOO_CONFIG_BAR
แล้วคำสั่งดังกล่าวพิมพ์ออกมาไม่ได้CONFIG_BAR
CONFIG_FOO_CONFIG_BAR
มีวิธีพิมพ์การจับคู่ครั้งแรกด้วย sed แต่มันค่อนข้างยุ่งยากเล็กน้อย:
sed -n -e 's/\(CONFIG_[a-zA-Z0-9_]*\).*/\n\1/' -e T -e 's/^.*\n//' -e p
(สมมติว่า sed ของคุณรองรับ\n
เพื่อหมายถึงบรรทัดใหม่ในs
ข้อความแทนที่) การทำงานนี้เนื่องจาก sed ค้นหาการจับคู่ที่เร็วที่สุดของ regexp และเราจะไม่พยายามจับคู่สิ่งที่นำหน้าCONFIG_…
บิต เนื่องจากไม่มีการขึ้นบรรทัดใหม่ภายในบรรทัดเราจึงสามารถใช้เป็นเครื่องหมายชั่วคราวได้ T
คำสั่งว่าจะให้ขึ้นถ้าก่อนหน้านี้s
คำสั่งไม่ตรงกับ
เมื่อคุณไม่สามารถหาวิธีที่จะทำบางสิ่งบางอย่างใน sed ให้หันไป awk คำสั่งต่อไปนี้พิมพ์การจับคู่ที่ยาวที่สุดเร็วที่สุดของ regexp:
awk 'match($0, /[0-9]+/) {print substr($0, RSTART, RLENGTH)}'
และถ้าคุณรู้สึกอยากทำให้มันง่ายขึ้นให้ใช้ Perl
perl -l -ne '/[0-9]+/ && print $&' # first match
perl -l -ne '/^.*([0-9]+)/ && print $1' # last match