Tìm văn bản giữa hai ký tự hoặc chuỗi cụ thể


17

Nói rằng tôi có những dòng như thế này:

*[234]*
*[23]*
*[1453]*

trong đó *đại diện cho bất kỳ chuỗi nào (ngoại trừ một chuỗi có dạng [number]). Làm cách nào tôi có thể phân tích các dòng này bằng tiện ích dòng lệnh và trích xuất số giữa các dấu ngoặc?

Tổng quát hơn, mà những công cụ này cut, sed, grephoặc awksẽ là thích hợp cho công việc như vậy?

Câu trả lời:


16

Nếu bạn có GNU grep, bạn có thể sử dụng -otùy chọn của nó để tìm kiếm một biểu thức chính quy và chỉ xuất ra phần phù hợp. (Việc triển khai grep khác chỉ có thể hiển thị toàn bộ dòng.) Nếu có một vài kết quả khớp trên một dòng, chúng được in trên các dòng riêng biệt.

grep -o '\[[0-9]*\]'

Nếu bạn chỉ muốn các chữ số chứ không phải dấu ngoặc thì khó hơn một chút; bạn cần sử dụng một xác nhận có độ rộng bằng 0: một biểu thức chính quy phù hợp với chuỗi rỗng, nhưng chỉ khi nó được đặt trước hoặc theo sau như trường hợp có thể, bằng một dấu ngoặc. Các xác nhận có độ rộng bằng không chỉ có sẵn trong cú pháp Perl.

grep -P -o '(?<=\[)[0-9]*(?=\])'

Với sed, bạn cần tắt in -nvà khớp toàn bộ dòng và chỉ giữ lại phần phù hợp. Nếu có một vài trận đấu có thể có trên một dòng, chỉ có trận đấu cuối cùng được in. Xem Trích xuất regex khớp với 'sed' mà không in các ký tự xung quanh để biết thêm chi tiết về cách sử dụng sed tại đây.

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

hoặc nếu bạn chỉ muốn các chữ số chứ không phải dấu ngoặc:

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

Không có grep -o, Perl là công cụ được lựa chọn ở đây nếu bạn muốn thứ gì đó vừa đơn giản vừa dễ hiểu. Trên mỗi dòng ( -n), nếu dòng chứa một kết quả khớp \[[0-9]*\], thì hãy in kết quả khớp đó ( $&) và một dòng mới ( -l).

perl -l -ne '/\[[0-9]*\]/ and print $&'

Nếu bạn chỉ muốn các chữ số, hãy đặt dấu ngoặc đơn trong biểu thức chính quy để phân định một nhóm và chỉ in nhóm đó.

perl -l -ne '/\[([0-9]*)\]/ and print $1'

PS Nếu bạn chỉ muốn yêu cầu một hoặc nhiều chữ số giữa các dấu ngoặc, hãy đổi [0-9]*thành [0-9][0-9]*hoặc thành [0-9]+Perl.


Tất cả đều tốt, ngoài việc anh ấy muốn "trích xuất số giữa các dấu ngoặc". Tôi nghĩ "ngoại trừ [number]" có nghĩa là ngoại trừ[0-9]
Peter.O

1
@ Peter.OI đã hiểu về Ngoại trừ [số], nghĩa là không có các phần khác của dòng đó. Nhưng tôi đã chỉnh sửa câu trả lời của mình để chỉ cách in các chữ số, chỉ trong trường hợp.
Gilles 'SO- ngừng trở thành ác quỷ'

1
Những perlkhẳng định regex trông thực sự hữu ích! Tôi đã đọc về chúng sau khi thấy bạn sử dụng cả hai xác nhận lùi và tiến, ngay cả trong grep (Tôi đã tắt đi thực tế là bạn có thể chọn một công cụ regex). Tôi sẽ dành thêm một chút thời gian cho regex của perl kể từ đây. Cảm ơn ... PS .. Tôi vừa đọc trong man grep... "Đây là một thử nghiệm cao và grep -P có thể cảnh báo các tính năng chưa được thực hiện." ... Tôi hy vọng điều đó không có nghĩa là không ổn định (?) ...
Peter.O

5

Bạn không thể làm điều đó với cut.

  1. tr -c -d '0123456789\012'
  2. sed 's/[^0-9]*//g'
  3. awk -F'[^0-9]+' '{ print $1$2$3 }'
  4. grep -o -E '[0-9]+'

tr là sự phù hợp tự nhiên nhất cho vấn đề và có thể sẽ chạy nhanh nhất, nhưng tôi nghĩ bạn sẽ cần đầu vào khổng lồ để tách bất kỳ tùy chọn nào về tốc độ.


Đối với sed, ^.*là tham lam và tiêu thụ tất cả trừ chữ số cuối cùng, và +cần phải \+sử dụng posix \([0-9][0-9]*\).... và trong mọi trường hợp cũng 's/[^0-9]*//g'hoạt động tốt, ... Thanks for the ví dụ tr -c`, nhưng đó không phải là dấu vết \012quá mức?
Peter.O

@Peter Cảm ơn bạn đã nắm bắt được điều đó. Tôi đã tuyên thệ tôi đã thử nghiệm ví dụ sed. :( Tôi đã thay đổi nó thành phiên bản của bạn. Về \012: nó là cần thiết nếu không trsẽ ăn các dòng mới.
Kyle Jones

Aha ... Tôi đã nhìn thấy nó như là \0, 1, 2(hoặc thậm chí \, 0, 1, 2). Có vẻ như tôi không đủ quan tâm đến bát phân .. Cảm ơn.
Peter.O

4

Nếu bạn muốn trích xuất một tập hợp các chữ số liên tiếp giữa các ký tự không có chữ số, tôi đoán sedawklà tốt nhất (mặc dù grepcũng có thể cung cấp cho bạn các ký tự trùng khớp):

sed: tất nhiên bạn có thể khớp với các chữ số, nhưng có lẽ thật thú vị khi làm ngược lại, loại bỏ các chữ số không (hoạt động khi chỉ có một số trên mỗi dòng):

$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344

grep: bạn có thể khớp các chữ số liên tiếp

$ echo nn3334nn | grep -o '[[:digit:]]*'
3344

Tôi không đưa ra một ví dụ awkvì tôi không có kinh nghiệm với nó; Điều thú vị cần lưu ý là, mặc dù sedlà một con dao thụy sĩ, grepmang đến cho bạn cách đơn giản hơn, dễ đọc hơn, cũng hoạt động với nhiều hơn một số trên mỗi dòng đầu vào ( -ochỉ in các phần khớp của đầu vào, mỗi phần trên dòng riêng của mình):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54

Cũng giống như một sự so sánh, đây là một sedeqivalent của "nhiều hơn một số trên mỗi dòng" dụ grep -o '[[:digit:]]*'. . . sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'... (+1)
Peter.O

2

Vì người ta nói rằng điều này không thể thực hiện được cut, tôi sẽ chỉ ra rằng có thể dễ dàng tạo ra một giải pháp ít nhất là không tệ hơn một số giải pháp khác, mặc dù tôi không tán thành việc sử dụng cutlà "tốt nhất" (hoặc thậm chí là một giải pháp đặc biệt tốt). Cần phải nói rằng bất kỳ giải pháp nào không tìm kiếm cụ thể *[]*xung quanh các chữ số sẽ đơn giản hóa các giả định và do đó dễ bị thất bại trên các ví dụ phức tạp hơn so với giải pháp được đưa ra bởi người hỏi (ví dụ: các chữ số bên ngoài *[]*không nên được hiển thị). Giải pháp này kiểm tra ít nhất cho các dấu ngoặc và nó cũng có thể được mở rộng để kiểm tra các dấu sao (còn lại là một bài tập cho người đọc):

cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'

Điều này làm cho việc sử dụng -dtùy chọn, trong đó chỉ định một dấu phân cách. Rõ ràng bạn cũng có thể chuyển thành cutbiểu thức thay vì đọc từ tệp. Mặc dù cutcó thể khá nhanh, vì nó đơn giản (không có công cụ regex), bạn phải gọi nó ít nhất hai lần (hoặc một vài lần nữa để kiểm tra *), điều này tạo ra một số chi phí xử lý. Ưu điểm thực sự của giải pháp này là nó khá dễ đọc, đặc biệt đối với người dùng thông thường không thành thạo các cấu trúc regex.

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.