Làm thế nào để in mẫu regex phù hợp bằng awk?


109

Khi sử dụng awk, tôi cần tìm một từ trong tệp khớp với mẫu regex.

Tôi chỉ muốn in từ phù hợp với mẫu.

Vì vậy, nếu trong dòng, tôi có:

xxx yyy zzz

Và mẫu:

/yyy/

Tôi chỉ muốn nhận được:

yyy

CHỈNH SỬA: cảm ơn kurumi tôi đã viết được một cái gì đó như thế này:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

và đây là những gì tôi cần :) cảm ơn rất nhiều!


1
@maxtaldykin Bạn có thể chuyển câu trả lời tự từ câu hỏi sang câu trả lời riêng biệt được không?
kenorb 14/02/18

2
Bạn không cần phải làm tmp=match($i, /regexp);if(tmp){}, bạn chỉ có thể làm if(tmp ~ $i){}~có nghĩa là "khớp với regexp".
JustinCB

Câu trả lời:


148

Đây là điều rất cơ bản

awk '/pattern/{ print $0 }' file

yêu cầu awktìm kiếm patternbằng cách sử dụng //, sau đó in ra dòng, theo mặc định được gọi là bản ghi, ký hiệu là $ 0. Ít nhất hãy đọc tài liệu .

Nếu bạn chỉ muốn in ra từ phù hợp.

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file

49
printlà hành động mặc định: awk '/pattern/' filesẽ đủ.
Johnsyweb

18
@Johnsyweb, vâng, tôi biết sự thật này. Đối với một người mới bắt đầu như marverix, nó có nghĩa là phải trực quan hơn.
kurumi

21
Tôi không nghi ngờ kiến ​​thức của bạn. Tuy nhiên, thông tin có thể hữu ích cho những người khác tìm thấy câu trả lời này.
Johnsyweb

2
NB: @marverix sẽ phải làm thêm một chút bài tập về nhà để làm cho for-loop hoạt động nếu (a) "yyy" là một biểu thức chính quy và không phải là một chuỗi thẳng và (b) nếu "yyy" đó không khớp với toàn bộ trường bên trong một kỷ lục.
Johnsyweb

8
Nó sẽ không $i=="yyy"; nó sẽ $i ~ /yyy/dành cho một biểu thức chính quy.
JustinCB

118

Có vẻ như bạn đang cố gắng mô phỏng grep -ohành vi của GNU . Điều này sẽ làm điều đó với điều kiện bạn chỉ muốn kết quả phù hợp đầu tiên trên mỗi dòng:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

Đây là một ví dụ, sử dụng awkviệc triển khai GNU ():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

Đọc về match, substr, RSTARTRLENGTHtrong awktay.

Sau đó, bạn có thể muốn mở rộng điều này để giải quyết nhiều trận đấu trên cùng một dòng.


NB: Để trả lời phần cuối cùng đó, tất cả các cấu trúc cần thiết đều nằm trong câu trả lời của kurumi và của riêng tôi.
Johnsyweb

Câu trả lời chính xác. Tôi chỉ muốn một lời giải thích ở đây vì tôi lười biếng. Nhưng đó là lý do tại sao tôi đang sử dụng AWK!
lukas.pukenis

Điều gì sẽ xảy ra nếu tôi muốn làm điều gì đó với kết quả khớp ngoại trừ việc in nó? Ví dụ: tôi muốn thêm tất cả các kết quả phù hợp vào mảng.
Evya2005

@ evya2005: Bạn có thể chỉ cần thay thế Ron print bằng bài tập mà bạn cần.
Johnsyweb

nó không làm việc cho tôi. chỉ tác phẩm in. bạn có thể chỉ cho tôi ví dụ?
Evya2005

36

gawk có thể lấy phần phù hợp của mọi dòng bằng cách sử dụng hành động này:

{ if (match($0,/your regexp/,m)) print m[0] }

match (string, regexp [, array]) Nếu xuất hiện mảng, nó sẽ bị xóa và khi đó phần tử thứ 0 của mảng được đặt thành toàn bộ phần của chuỗi được so khớp bởi regexp. Nếu regexp chứa các dấu ngoặc đơn, thì các phần tử được lập chỉ mục số nguyên của mảng được đặt để chứa phần chuỗi khớp với biểu thức con được đặt trong ngoặc đơn tương ứng. http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions


13

Nếu bạn chỉ quan tâm đến dòng đầu vào cuối cùng và bạn mong đợi chỉ tìm thấy một kết quả phù hợp (ví dụ: một phần của dòng tóm tắt của lệnh shell), bạn cũng có thể thử mã rất nhỏ gọn này, được thông qua từ Cách in kết quả khớp regexp sử dụng `awk`? :

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

Hoặc phiên bản phức tạp hơn với kết quả một phần:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

Cảnh báo: awk match()hàm có ba đối số chỉ tồn tại trong gawk, không tồn tại trongmawk

Dưới đây là một giải pháp tốt đẹp bằng cách sử dụng regex lookbehind trong grepthay vì awk. Giải pháp này có các yêu cầu thấp hơn đối với cài đặt của bạn:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b

Tại sao bạn lại thêm "tail -n1"? Điều này sẽ hoạt động tốt nếu không có nó, không?
Arthur Accioly

1
@ArthurAccioly Đúng. Tôi đã sử dụng thuật ngữ này để trích xuất thời gian khứ hồi trung bình từ một cuộc gọi ping, đó là nguồn gốc của nó. hài hước rằng phải mất 4 năm để khám phá nó;)
Daniel Alder

12

Nếu Perl là một tùy chọn, bạn có thể thử điều này:

perl -lne 'print $1 if /(regex)/' file

Để triển khai đối sánh không phân biệt chữ hoa chữ thường, hãy thêm công cụ isửa đổi

perl -lne 'print $1 if /(regex)/i' file

Để in mọi thứ SAU trận đấu:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

Để in trận đấu và mọi thứ sau trận đấu:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile

3

Sử dụng sed cũng có thể trở nên thanh lịch trong tình huống này. Ví dụ (thay thế dòng bằng nhóm phù hợp "yyy" từ dòng):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

Trang hướng dẫn liên quan: https://www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions


Đối với những người không phải gnu sed, giải pháp là một cái gì đó như thế này:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin

1
@GrigoryEntin - bsd sed hoạt động tốt với câu trả lời ban đầu. Công tắc regex mở rộng được hỗ trợ bởi POSIX là -E, nhưng trong FreeBSD ít nhất -r giống với -E (-r được thêm vào năm 2010). Dù sao, hãy thử với -E (gnu sed được thêm -E trong 4.3)
Juan

3

Lạc đề, điều này cũng có thể được thực hiện bằng cách sử dụng grep, chỉ cần đăng nó ở đây trong trường hợp có ai đang tìm kiếm giải pháp grep

echo 'xxx yyy zzze ' | grep -oE 'yyy'

Cách đơn giản để lấy nó ngay cả với regex. Chính xác những gì tôi cần. Cảm ơn!
Marquee

Điều này làm việc cho tôi; Trường hợp của tôi như sau: echo "web_port = 8080, shutdown_port = 8005" | grep -oE "web_port = [0-9] +" # return 8080
Robb Tsang

0

Nếu bạn biết văn bản / mẫu bạn đang tìm (ví dụ: "yyy") ở cột nào, bạn chỉ cần kiểm tra cột cụ thể đó để xem nó có khớp hay không và in nó.

Ví dụ: được cung cấp một tệp có nội dung sau, (được gọi là asdf.txt )

xxx yyy zzz

để chỉ in cột thứ hai nếu nó khớp với mẫu "yyy", bạn có thể làm như sau:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

Lưu ý rằng điều này cũng sẽ khớp về cơ bản với bất kỳ dòng nào trong đó cột thứ hai có "yyy" trong đó, như sau:

xxx yyyz zzz
xxx zyyyz
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.