Trích xuất chuỗi con bằng regexp trong bash đơn giản


97

Tôi đang cố gắng trích xuất thời gian từ một chuỗi bằng cách sử dụng bash và tôi đang gặp khó khăn khi tìm ra nó.

Chuỗi của tôi là như thế này:

US/Central - 10:26 PM (CST)

Và tôi muốn trích xuất 10:26một phần.

Có ai biết cách làm điều này chỉ với bash - mà không sử dụng sed, awk, v.v. không?

Giống như, trong PHP tôi sẽ sử dụng - không phải là cách tốt nhất, nhưng nó hoạt động - đại loại như:

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

Cảm ơn mọi sự giúp đỡ, ngay cả khi câu trả lời sử dụng sed hoặc awk

Câu trả lời:


207

Sử dụng nguyên chất :

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

một giải pháp khác với bash regex:

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

một giải pháp khác bằng cách sử dụng grepvà xem xét xung quanh regex nâng cao:

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

một giải pháp khác sử dụng sed:

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

một giải pháp khác sử dụng perl:

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

và cái cuối cùng sử dụng awk:

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

Mát mẻ! Bất kỳ cơ hội nào tôi cũng sử dụng dấu gạch nối "-" trong mẫu? vì grep trả về một số trận đấu, và tôi chỉ quan tâm đến một trong đó có gạch nối và sau đó một không gian rồi chọn giờ .....
andrux

Tôi có thể có giải pháp perl, nhưng đó là một điểm cộng tuyệt vời. Cảm ơn!
andrux

thêm một awk cho vui =)
Gilles Quenot

1
Cảm ơn bạn đã cho tôi biết \ K "thủ thuật". grep với cú pháp perl thực sự mạnh mẽ.
Marco Sulla

1
Tôi thích sedphiên bản này nhưng muốn cảnh báo những người khác rằng sedkhông nhất thiết phải sử dụng công cụ +sửa đổi. Một cách để giải quyết là sử dụng công cụ {1, }sửa đổi để khớp với một hoặc nhiều.
CodeBrew

89
    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
Tôi cảm thấy như điều này khiến tôi trở thành một bậc thầy quyến rũ ngay lập tức. Một lựa chọn tốt mà tôi có thể chỉnh sửa tốt hơn là chín lựa chọn mà tôi không hiểu.
Noumenon

Cảm ơn bạn đã giải thích chi tiết, giúp tránh các bài đăng "làm cách nào để tôi regexp XXXX" trong tương lai.
studgeek

4
Bạn có thể giải thích lý do tại sao trước tiên bạn ngừng in với -nsau đó yêu cầu in lại với /p? Sẽ không giống nhau nếu bỏ -ncờ và bỏ /pchỉ thị? Cảm ơn.
Victor Zamanian

Câu trả lời chính xác ! Cảm ơn sự giúp đỡ của bạn :-)
Bruno Lavit

1
@VictorZamanian từ đây : "Theo mặc định, sed in mọi dòng. Nếu nó thay thế, văn bản mới sẽ được in thay vì văn bản cũ. Nếu bạn sử dụng đối số tùy chọn cho sed," sed -n ", nó sẽ không, theo mặc định, in bất kỳ dòng mới nào. ... Khi tùy chọn "-n" được sử dụng, cờ "p" sẽ làm cho dòng đã sửa đổi được in. "
tdashroy

26

Kỹ thuật chặt nhanh chóng bẩn, không có tạp chất, độ bền thấp

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

5
Điều đó thật là bẩn thỉu kinh tởm đến nỗi tôi xấu hổ vì chính mình cũng không nghĩ ra. +1 | read zone dash time apm zonecũng hoạt động
Orwellophile

Rất sạch sẽ và tránh các cuộc gọi đến các chương trình bên ngoài.
Victor Zamanian

8
Xin chào, điều này sẽ hữu ích hơn gấp 10 lần nếu nó bao gồm tham chiếu đến tài liệu bổ sung hoặc một số tên xung quanh kỹ thuật để mọi người có thể bắt đầu và nghiên cứu thêm. Đối với những người quan tâm, đây là thao tác chuỗi bash và bạn có thể tìm thêm chi tiết tại đây: tldp.org/LDP/abs/html/string-manipulation.html
Pedro Mata-Mouros

0

Nếu chuỗi của bạn là

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

sau đó

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

sẽ thực hiện công việc.


1
hoặc cut -c14-18tất nhiên chỉ miễn là vị trí ký tự không thay đổi. điều này sẽ không xảy ra nếu Múi giờ được cố định.
Markus

Câu hỏi của Ngài được đặt ra cho regex không phải để cắt
indrajit narvekar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.