AWK: Truy cập nhóm bị bắt từ mẫu đường


229

Nếu tôi có một lệnh awk

pattern { ... }

và mẫu sử dụng một nhóm bắt giữ, làm thế nào tôi có thể truy cập chuỗi để được bắt trong khối?



Đôi khi (trong trường hợp đơn giản) có thể điều chỉnh dấu tách trường ( FS) và chọn thứ mà người ta muốn khớp với a $field. Định dạng trước đầu vào cũng có thể giúp.
Krzysztof Jabłoński

1
Có một câu trả lời tốt hơn cho câu hỏi trùng lặp.
Phường Samuel Edwin

2
Samuel Edwin Ward: Đó cũng là một câu trả lời hay! Nhưng nó cũng đòi hỏi gawk(vì nó sử dụng gensub).
hung hăng

Câu trả lời:


176

Đó là một cuộc dạo chơi xuống làn nhớ ...

Tôi đã thay thế awk bằng perl một thời gian dài trước đây.

Rõ ràng công cụ biểu thức chính quy AWK không bắt được các nhóm của nó.

bạn có thể cân nhắc sử dụng một cái gì đó như:

perl -n -e'/test(\d+)/ && print $1'

cờ -n gây ra lỗi perl lặp trên mỗi dòng như awk.


3
Rõ ràng có người không đồng ý. Trang web này có từ năm 2005: tek-tips.com/faqs.cfm?fid=5674 Nó xác nhận rằng bạn không thể sử dụng lại các nhóm phù hợp trong awk.
Peter Tillemans

3
Tôi thích 'perl -n -p -e ...' hơn awk cho hầu hết các trường hợp sử dụng, vì nó linh hoạt hơn, mạnh mẽ hơn và theo cú pháp của tôi.
Peter Tillemans

15
gawk! = awk. Chúng là các công cụ khác nhau và gawkkhông có sẵn theo mặc định ở hầu hết các nơi.
Oli

6
OP đặc biệt yêu cầu một giải pháp awk, vì vậy tôi không nghĩ đây là câu trả lời.
Joppe

6
@Joppe bạn không thể đưa ra giải pháp awk nếu không có giải pháp. Trong dòng 3 tôi giải thích rằng AWK không hỗ trợ các nhóm bắt giữ và tôi đã đưa ra một giải pháp thay thế, điều mà OP rõ ràng đánh giá cao vì câu trả lời này đã được chấp nhận. Làm thế nào tôi có thể trả lời tốt hơn câu hỏi này?
Peter Tillemans

335

Với gawk, bạn có thể sử dụng matchchức năng để chụp các nhóm được ngoặc đơn.

gawk 'match($0, pattern, ary) {print ary[1]}' 

thí dụ:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}' 

đầu ra cd.

Lưu ý việc sử dụng cụ thể của gawk mà thực hiện các tính năng trong câu hỏi.

Đối với một thay thế di động, bạn có thể đạt được kết quả tương tự với match()substr.

thí dụ:

echo "abcdef" | awk 'match($0, /b[^e]*/) {print substr($0, RSTART+1, RLENGTH-1)}'

đầu ra cd.


4
Có, các biến thể gxxx có rất nhiều tính tốt và sức mạnh của GNU.
Peter Tillemans

Hoạt động trong BusyBox awk là tốt.
MrMas

32

Đây là thứ tôi cần mọi lúc nên tôi đã tạo một hàm bash cho nó. Nó dựa trên câu trả lời của glenn jackman.

Định nghĩa

Thêm phần này vào .bash_profile của bạn, v.v.

function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }

Sử dụng

Chụp regex cho mỗi dòng trong tệp

$ cat filename | regex '.*'

Chụp nhóm chụp regex thứ nhất cho mỗi dòng trong tệp

$ cat filename | regex '(.*)' 1

2
Nó khác với việc sử dụng grep -onhư thế nào?
bfontaine

@bfontaine Có thể grep -oxuất các nhóm bị bắt?
Olle Härstedt

1
@ OlleHärstedt Không, không thể. Nó chỉ bao gồm trường hợp sử dụng của bạn khi bạn không có nhóm chụp. Trong trường hợp đó, nó trở nên xấu xí với dây xích grep -o.
bfontaine

15

Bạn có thể sử dụng GNU awk:

$ cat hta
RewriteCond %{HTTP_HOST} !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m) { print m[1]; }' < hta
http://www.mysite.net/

12
+1. Ngoài ra, với bất kỳ awk nào:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ed Morton


1
Ed Morton: đó xứng đáng là câu trả lời cấp cao nhất mà tôi muốn nói. chỉnh sửa: uhm ... bản in đó RewriteRule (.*) http://www.mysite.net/$cho tôi, nhiều hơn nhóm phụ.
hung hăng


4

Bạn cũng có thể mô phỏng việc chụp trong vanilla awk mà không cần tiện ích mở rộng. Nó không trực quan mặc dù:

bước 1. sử dụng gensub để bao quanh các kết quả khớp với một số ký tự không xuất hiện trong chuỗi của bạn. bước 2. Sử dụng phân chia chống lại nhân vật. Bước 3. Mọi phần tử khác trong mảng được tách là nhóm chụp của bạn.

$ echo 'ab cb quảng cáo' | awk '{split (gensub (/ a ./, SUBSEP "&" SUBSEP, "g", $ 0), cap, SUBSEP); nắp in [2] "|" nắp [4]; } '
ab | quảng cáo

3
Tôi gần như chắc chắn đó gensublà một gawkchức năng cụ thể. Bạn nhận được gì từ awk của mình nếu bạn gõ awk --version; -?). Chúc mọi người may mắn.
shellter

6
Tôi hoàn toàn chắc chắn rằng gensub là một con chim ưng, mặc dù BusyBox awk cũng có nó. Câu trả lời này cũng có thể được thực hiện bằng gsub, mặc dù:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim

3
gensub () là một phần mở rộng của gawk, hướng dẫn của gawk nói rõ ràng như vậy. Các biến thể awk khác cũng có thể thực hiện nó, nhưng nó vẫn không phải là POSIX. Hãy thử gawk --poseix '{gsub (...)}' và nó sẽ phàn nàn
MestreLion

2
@MestreLion, ý bạn là nó sẽ phàn nàn gawk --posix '{gensub(...)}'.
dubiousjim

1
Mặc dù bạn đã sai về việc POSIX awkgensubchức năng, ví dụ của bạn được áp dụng cho một kịch bản rất hạn chế: toàn bộ mô hình được nhóm lại, nó không thể khớp với một cái gì đó giống như key=(value)khi tôi chỉ trích xuất các valuephần.
Meow

2

Tôi đã vật lộn một chút với việc đưa ra một hàm bash bao bọc câu trả lời của Peter Tillemans nhưng đây là những gì tôi nghĩ ra:

hàm regex {perl -n -e "/ $ 1 / && printf \"% s \ n \ "," '$ 1'}

Tôi thấy điều này hoạt động tốt hơn hàm bash dựa trên awk của opsb cho đối số biểu thức chính quy sau đây, vì tôi không muốn in "ms".

'([0-9]*)ms$'

Tôi thích giải pháp này, vì bạn có thể thấy các phần của nhóm phân định việc chụp, đồng thời bỏ qua chúng. Tuy nhiên, ai đó có thể làm thế nào điều này hoạt động? Tôi không thể sử dụng cú pháp perl này để hoạt động chính xác trong BASH, vì tôi không hiểu rõ về nó - đặc biệt là các dấu ngoặc kép / dấu ngoặc đơn xung quanh$1
Demis

Nó không phải là điều tôi đã làm trước đây hoặc kể từ đó, nhưng nhìn lại những gì nó đang làm là nối hai chuỗi, chuỗi đầu tiên nằm trong dấu ngoặc kép (chuỗi đầu tiên này chứa dấu ngoặc kép được nhúng với dấu gạch chéo ngược) và chuỗi thứ hai nằm trong dấu ngoặc đơn . Sau đó, kết quả của phép nối đó được cung cấp dưới dạng đối số cho perl -e. Ngoài ra, bạn cần biết rằng $ 1 đầu tiên (một trong hai dấu ngoặc kép) được thay thế bằng đối số đầu tiên cho hàm, trong khi $ 1 thứ hai (một trong các dấu ngoặc đơn) không được chạm tới. Xem ví dụ này
wytten

Tôi hiểu rồi, điều đó có ý nghĩa hơn một chút bây giờ. Vì vậy, trong lệnh perl là định nghĩa bắt khớp / nhóm regex ở đâu? Tôi thấy bạn đã viết '([0-9]*)ms$'- đó có phải là một đối số (và chuỗi đối số khác) không? Và đầu ra từ perl -eđang được chèn vào printflệnh của bash , để thay thế %s, có đúng không? Cảm ơn, tôi hy vọng sẽ sử dụng này.
Demis

1
Bạn chuyển một biểu thức chính quy được đặt trong các dấu ngoặc đơn làm đối số duy nhất cho hàm bash regex. Ví dụ
wytten
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.