sed: chỉ in nhóm phù hợp


133

Tôi muốn lấy hai số cuối (một int, một float; theo sau là khoảng trắng tùy chọn) và chỉ in chúng.

Thí dụ:

foo bar <foo> bla 1 2 3.4

Nên in:

2 3.4

Cho đến nay, tôi có những điều sau đây:

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

sẽ cho tôi

foo bar <foo> bla 1 replacement

Tuy nhiên, nếu tôi cố gắng thay thế nó bằng nhóm 1, toàn bộ dòng được in.

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

Làm cách nào tôi chỉ có thể in phần của dòng phù hợp với biểu thức chính quy trong nhóm?

Câu trả lời:


138

Khớp toàn bộ dòng, vì vậy hãy thêm một .*ở đầu regex của bạn. Điều này làm cho toàn bộ dòng được thay thế bằng nội dung của nhóm

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
Tôi đã phải thêm -rtùy chọn hoặc `--regrec-Extended` nếu không tôi nhận được invalid reference \1 on lỗi RHS` của lệnh '.
Daniel Sokolowski

15
@DanielSokolowski Tôi nghĩ bạn sẽ gặp lỗi đó nếu bạn sử dụng ()thay vì \(\).
Daniel Darabos

3
Ngoài ra, hãy nhớ thêm .*vào cuối regrec nếu chuỗi bạn muốn trích xuất không phải lúc nào cũng ở cuối dòng.
Teemu Leisti

3
Điều này sẽ không hiệu quả với tôi vì .*tham lam và sed không có người không tham lam.*?
sondra.kinsey

@DanielDarabos Chỉ cần đề cập đến điều đó ()sẽ không gây ra lỗi trong Ubuntu 16.04. Vì vậy, tôi nghĩ rằng nhận xét này đã lỗi thời.
Li haonan

72

grep là công cụ phù hợp để giải nén.

sử dụng ví dụ của bạn và regex của bạn:

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

12
tuyệt vời cho toàn bộ nhóm, mặc dù sed là cần thiết cho các nhóm riêng lẻ
jozxyqk

grep -o không port trên các hệ thống chạy msysgit nhưng sed thì có.
cchamberlain

Xem câu hỏi được liên kết bởi @jozxyqk để biết câu trả lời sử dụng nhìn về phía trước và nhìn phía sau để giải quyết vấn đề này bằng grep.
Joachim Breitner

Bạn có thể trích xuất một nhóm từ một mẫu với grep -ocác cuộc gọi đường ống . stackoverflow.com/a/58314379/117471
Bruno Bronosky

12

Và cho một lựa chọn khác, tôi sẽ đi với awk!

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

Điều này sẽ phân chia đầu vào (Tôi đang sử dụng STDIN ở đây, nhưng đầu vào của bạn có thể dễ dàng là một tệp) trên các khoảng trắng, sau đó in ra trường cuối cùng, rồi đến trường cuối cùng. Các $NFbiến giữ số lượng các trường được tìm thấy sau khi phát nổ trên không gian.

Lợi ích của việc này là không thành vấn đề nếu những gì xảy ra trước hai trường cuối cùng thay đổi, miễn là bạn chỉ muốn hai trường cuối cùng sẽ tiếp tục hoạt động.


3

Lệnh cắt được thiết kế cho tình huống chính xác này. Nó sẽ "cắt" trên bất kỳ dấu phân cách nào và sau đó bạn có thể chỉ định khối nào sẽ được xuất.

Ví dụ: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Sẽ dẫn đến kết quả đầu ra của: 2 3.4

-d đặt dấu phân cách

-f chọn phạm vi 'trường' để xuất ra, trong trường hợp này, đó là đoạn thứ 6 đến thứ 7 của chuỗi gốc. Bạn cũng có thể chỉ định phạm vi dưới dạng danh sách, chẳng hạn như 6,7.


Để chỉ in một số cột nhất định, hãy chuyển đếnawk '{ print $2" "$6 }'
Nurettin

@nurettin Tôi nghĩ rằng nhận xét của bạn có thể có nghĩa là một trong những câu trả lời awk.
carlin.scott

Tôi đã thử cắt khi tôi truy cập trang này và nhận ra những hạn chế của nó và quyết định viết một phiên bản tổng quát hơn trong awk thay vì như một bình luận để cải thiện chất lượng của bài đăng này.
Nurettin

1
Vâng, tôi nghĩ rằng đó là một câu trả lời khác liên quan đến awk. Lệnh cắt để làm những gì bạn đã viết là:cut -d " " -f 2,6
carlin.scott

à, tôi không biết điều đó, tôi nghĩ bạn chỉ có thể đưa ra phạm vi. Cảm ơn vì điều đó.
Nurettin

2

Tôi đồng ý với @kent rằng điều này rất phù hợp cho grep -o. Nếu bạn cần trích xuất một nhóm trong một mẫu, bạn có thể thực hiện nó với grep thứ 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
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.