แยกสตริงย่อยโดยใช้ regexp ใน bash ธรรมดา


101

ฉันพยายามดึงเวลาออกจากสตริงโดยใช้ bash และฉันก็มีปัญหาในการหามันออกมา

สตริงของฉันเป็นแบบนี้:

US/Central - 10:26 PM (CST)

และผมต้องการแยก10:26ส่วน

มีใครรู้วิธีทำสิ่งนี้ด้วยการทุบตีโดยไม่ใช้ sed, awk ฯลฯ บ้างไหม?

เช่นใน PHP ฉันจะใช้ - ไม่ใช่วิธีที่ดีที่สุด แต่ได้ผล - สิ่งที่ชอบ:

preg_match( ""(\d{2}\:\d{2}) PM \(CST\)"", "US/Central - 10:26 PM (CST)", $matches );

ขอบคุณสำหรับความช่วยเหลือแม้ว่าคำตอบจะใช้ sed หรือ awk

คำตอบ:


214

โดยใช้บริสุทธิ์ :

$ cat file.txt
US/Central - 10:26 PM (CST)
$ while read a b time x; do [[ $b == - ]] && echo $time; done < file.txt

วิธีแก้ปัญหาอื่นด้วย bash regex:

$ [[ "US/Central - 10:26 PM (CST)" =~ -[[:space:]]*([0-9]{2}:[0-9]{2}) ]] &&
    echo ${BASH_REMATCH[1]}

โซลูชันอื่นที่ใช้grepและมองไปรอบ ๆ regex ขั้นสูง:

$ echo "US/Central - 10:26 PM (CST)" | grep -oP "\-\s+\K\d{2}:\d{2}"

วิธีอื่นโดยใช้ sed:

$ echo "US/Central - 10:26 PM (CST)" |
    sed 's/.*\- *\([0-9]\{2\}:[0-9]\{2\}\).*/\1/'

วิธีอื่นโดยใช้ perl:

$ echo "US/Central - 10:26 PM (CST)" |
    perl -lne 'print $& if /\-\s+\K\d{2}:\d{2}/'

และอันสุดท้ายโดยใช้ awk:

$ echo "US/Central - 10:26 PM (CST)" |
    awk '{for (i=0; i<=NF; i++){if ($i == "-"){print $(i+1);exit}}}'

เย็น! มีโอกาสใช้ยัติภังค์ "-" ในรูปแบบด้วยหรือไม่ เพราะ grep ส่งคืนการจับคู่บางรายการและฉันสนใจเฉพาะอันที่มียัติภังค์แล้วเว้นวรรคแล้วเวลา .....
andrux

ฉันอาจจะมีวิธีแก้ปัญหา perl แต่ก็เป็นข้อดีที่ยอดเยี่ยม ขอบคุณ!
andrux

เพิ่ม awk one เพื่อความสนุก =)
Gilles Quenot

1
ขอขอบคุณที่แจ้งให้เราทราบ \ K "เคล็ดลับ" grep กับไวยากรณ์ perl มีประสิทธิภาพมาก
Marco Sulla

1
ฉันชอบsedเวอร์ชันนี้ แต่ต้องการเตือนคนอื่น ๆ ที่sedไม่จำเป็นต้องใช้+ตัวปรับแต่ง วิธีหนึ่งในการแก้ไขคือใช้{1, }ตัวปรับแต่งเพื่อจับคู่อย่างน้อยหนึ่งอย่าง
CodeBrew

93
    echo "US/Central - 10:26 PM (CST)" | sed -n "s/^.*-\s*\(\S*\).*$/\1/p"

-n      suppress printing
s       substitute
^.*     anything at the beginning
-       up until the dash
\s*     any space characters (any whitespace character)
\(      start capture group
\S*     any non-space characters
\)      end capture group
.*$     anything at the end
\1      substitute 1st capture group for everything on line
p       print it

8
ฉันรู้สึกว่าสิ่งนี้ทำให้ฉันกลายเป็นเซียนในทันที ทางเลือกหนึ่งที่ดีที่ฉันสามารถปรับแต่งได้ดีกว่าเก้าฉันไม่เข้าใจ
Noumenon

ขอขอบคุณสำหรับคำอธิบายโดยละเอียดจะช่วยหลีกเลี่ยงโพสต์ "ฉันจะ regexp XXXX" ในอนาคตได้อย่างไร
studgeek

4
คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงระงับการพิมพ์-nก่อนแล้วจึงขอพิมพ์อีกครั้งด้วย/p? การละเว้น-nแฟล็กและละเว้น/pคำสั่งจะไม่เหมือนกันหรือ? ขอบคุณ.
Victor Zamanian

ตอบโจทย์มาก! ขอบคุณสำหรับความช่วยเหลือ :-)
Bruno Lavit

1
@VictorZamanian จากที่นี่ : "โดยค่าเริ่มต้น sed จะพิมพ์ทุกบรรทัดหากทำการแทนที่ข้อความใหม่จะถูกพิมพ์แทนข้อความเก่าหากคุณใช้อาร์กิวเมนต์ที่เป็นตัวเลือกเพื่อ sed" sed -n "จะไม่ โดยค่าเริ่มต้นให้พิมพ์บรรทัดใหม่ ... เมื่อใช้ตัวเลือก "-n" แฟล็ก "p" จะทำให้มีการพิมพ์บรรทัดที่แก้ไข "
tdashroy

27

เทคนิคการสับสับที่สกปรกปราศจาก regex และมีความทนทานต่ำ

string="US/Central - 10:26 PM (CST)"
etime="${string% [AP]M*}"
etime="${etime#* - }"

6
มันสกปรกอย่างน่าขยะแขยงจนฉันรู้สึกละอายใจไม่ได้คิดไปเอง +1 ได้| read zone dash time apm zoneผลเช่นกัน
Orwellophile

สะอาดมากและหลีกเลี่ยงการโทรไปยังโปรแกรมภายนอก
Victor Zamanian

12
สวัสดีนี่จะมีประโยชน์มากขึ้น 10 เท่าหากมีการอ้างอิงถึงเอกสารเพิ่มเติมหรือชื่อบางส่วนเกี่ยวกับเทคนิคเพื่อให้ผู้คนสามารถออกไปค้นคว้าเพิ่มเติมได้ สำหรับผู้สนใจนี่คือการจัดการสตริง bash และคุณสามารถดูรายละเอียดเพิ่มเติมได้ที่นี่: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros

1

ถ้าสตริงของคุณคือ

foo="US/Central - 10:26 PM (CST)"

แล้ว

echo "${foo}" | cut -d ' ' -f3

จะทำงาน


1
หรือcut -c14-18แน่นอนตราบเท่าที่ตำแหน่งตัวละครไม่เปลี่ยนแปลง ซึ่งไม่ควรเกิดขึ้นหากมีการกำหนดเขตเวลา
Markus

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