Không cần toàn bộ dòng, chỉ cần khớp từ biểu thức chính quy


13

Tôi chỉ cần lấy trận đấu từ một biểu thức thông thường:

$ cat myfile.txt | SOMETHING_HERE "/(\w).+/"

Đầu ra phải là những gì được khớp, bên trong dấu ngoặc đơn.

Đừng nghĩ rằng tôi có thể sử dụng grep vì nó phù hợp với toàn bộ dòng.

Xin vui lòng cho tôi biết làm thế nào để làm điều này.

Câu trả lời:


11

2 điều:

  • Như @Rory đã nêu, bạn cần có -otùy chọn, do đó, chỉ có trận đấu được in (thay vì toàn bộ dòng)
  • Ngoài ra, bạn -Pchọn tùy chọn, để sử dụng các biểu thức chính quy Perl, bao gồm các yếu tố hữu ích như Nhìn về phía trước (?= )Nhìn phía sau (?<= ) , chúng tìm các bộ phận, nhưng không thực sự khớp và in chúng.

Nếu bạn chỉ muốn phần bên trong parensis được khớp:

grep -oP '(?<=\/\()\w(?=\).+\/)' myfile.txt

nếu tệp chứa sting /(a)5667/, grep sẽ in 'a', bởi vì:

  • /(được tìm thấy bởi \/\(, nhưng vì chúng ở phía sau (?<= ) nên chúng không được báo cáo
  • ađược khớp bởi \wvà do đó được in (vì -o)
  • )5667/được tìm thấy b < \).+\/, nhưng vì chúng ở phía trước (?= ) nên chúng không được báo cáo

17

Sử dụng -otùy chọn trong grep.

Ví dụ:

$ echo "foobarbaz" | grep -o 'b[aeiou]r'
bar

4
Thật đau buồn ... Bạn có biết bao nhiêu lần tôi vật lộn với những phản sedứng ngược để làm điều đó không?
Insyte

9
Tùy chọn o để grep / egrep chỉ trả về những gì khớp với toàn bộ biểu thức chính quy, không chỉ những gì trong () như anh ta yêu cầu.
Kyle Brandt

1
Tuy nhiên, đó là một điều rất tốt để biết dù sao :-)
Kyle Brandt

2
@KyleBrandt: Để chỉ khớp một phần (ví dụ: các đường dẫn), có thể đánh dấu phần còn lại bằng một cái nhìn phía trước hoặc nhìn phía sau: (? <=) Và (? =)
DrYak

6
    sed -n "s/^.*\(captureThis\).*$/\1/p"

-n      don't print lines
s       substitute
^.*     matches anything before the captureThis 
\( \)   capture everything between and assign it to \1 
.*$     matches anything after the captureThis 
\1      replace everything with captureThis 
p       print it

4

Nếu bạn chỉ muốn những gì trong ngoặc đơn, bạn cần một cái gì đó hỗ trợ chụp các kết quả phụ (Các nhóm bắt giữ được đặt tên hoặc đánh số). Tôi không nghĩ grep hoặc egrep có thể làm điều này, perl và sed có thể. Ví dụ: với perl:

Nếu một tệp có tên foo có một dòng như sau:

/adsdds      /

Và bạn làm:

perl -nle 'print $1 if /\/(\w).+\//' foo

Chữ a được trả lại. Đó có thể không phải là những gì bạn muốn mặc dù. Nếu bạn nói với chúng tôi những gì bạn đang cố gắng để phù hợp, bạn có thể nhận được sự giúp đỡ tốt hơn. $ 1 là bất cứ thứ gì được ghi lại trong tập ngoặc đơn đầu tiên. $ 2 sẽ là bộ thứ hai, v.v.


Tôi chỉ cố gắng để phù hợp với những gì trong ngoặc đơn. Có vẻ như chuyển nó cho một perl hoặc một tập lệnh php có thể là câu trả lời.
Alex L

4

Vì bạn đã gắn thẻ câu hỏi của mình là bash ngoài shell , nên có một giải pháp khác bên cạnh grep :

Bash có công cụ biểu thức chính quy của nó kể từ phiên bản 3.0, sử dụng =~toán tử, giống như Perl.

Bây giờ, đưa ra mã sau đây:

#!/bin/bash
DATA="test <Lane>8</Lane>"

if [[ "$DATA" =~ \<Lane\>([[:digit:]]+)\<\/Lane\> ]]; then
        echo $BASH_REMATCH
        echo ${BASH_REMATCH[1]}
fi
  • Lưu ý rằng bạn phải gọi nó như là bashvà không chỉ shđể có được tất cả các tiện ích mở rộng
  • $BASH_REMATCH sẽ cung cấp cho toàn bộ chuỗi như khớp với toàn bộ biểu thức chính quy, vì vậy <Lane>8</Lane>
  • ${BASH_REMATCH[1]} sẽ cho phần khớp với nhóm 1, do đó chỉ 8

Gửi @DrYak, tôi hy vọng bạn không phân tích cú pháp XML bằng regex tại đây .. :)
joonas.fi

Nó thậm chí còn tồi tệ hơn. Tôi đang phân tích một hỗn hợp khủng khiếp của dữ liệu XML và FASTA (cả hai đều sử dụng >biểu tượng cho các mục đích hoàn toàn khác nhau) như được phát hiện ra bởi phần mềm sắp xếp quy mô nhanh Sans Sans . Tất nhiên cả hai định dạng được phun xen kẽ mà không có bất kỳ thoát. Vì vậy, không thể ném một số thư viện XML tiêu chuẩn vào đây. Và tôi đang sử dụng Bash regex tại thời điểm này của mã bởi vì tôi chỉ cần trích xuất một vài dữ liệu và 2 regex thực hiện công việc tốt hơn cho tôi so với việc viết một trình phân tích cú pháp chuyên dụng cho mớ hỗn độn này. #LifeInBioinformatics
DrYak

Nói cách khác: có một điểm mà việc trích xuất 1 số đơn giản dễ thực hiện hơn với regex rathan so với nhảy toàn bộ tango XML
DrYak

Hah, gotcha! :)
joonas.fi

2

Giả sử tập tin chứa:

$ cat file
Text-here>xyz</more text

Và bạn muốn (các) ký tự nằm giữa ></, bạn có thể sử dụng một trong hai:

grep -oP '.*\K(?<=>)\w+(?=<\/)' file
sed -nE 's:^.*>(\w+)</.*$:\1:p' file
awk '{print(gensub("^.*>(\\w+)</.*$","\\1","g"))}' file
perl -nle 'print $1 if />(\w+)<\//' file

Tất cả sẽ in một chuỗi "xyz".

Nếu bạn muốn chụp các chữ số của dòng này:

$ cat file
Text-<here>1234</text>-ends

grep -oP '.*\K(?<=>)[0-9]+(?=<\/)' file
sed -E 's:^.*>([0-9]+)</.*$:\1:' file
awk '{print(gensub(".*>([0-9]+)</.*","\\1","g"))}' file
perl -nle 'print $1 if />([0-9]+)<\//' file


Đối với tôi, điều quan trọng là nhận ra \ d không hoạt động với sed. Có một lý do bạn sử dụng [0-9] + ở đó. :)
dùng27432

@ user27423 Không, nhưng các lớp ký tự POSIX ( đọc đau , đọc dễ chịu ) làm : echo 'Text-<here>1234</text>-ends' | sed -E 's|.*>([[:digit:]]+)<.*|\1|'. Trong một số trường hợp (ví dụ [0-9]so với [[:digit:]]) họ không giúp đỡ mức độ dễ đọc, trong những trường hợp khác tôi nghĩ họ làm (ví dụ [ \t\n\r\f\v]so với [:space:]).
Samuel Harmer

0

Điều này sẽ thực hiện những gì bạn đang yêu cầu, nhưng tôi không nghĩ đó là những gì bạn thực sự muốn. Tôi đặt .*phía trước của regex để ăn bất cứ thứ gì trước trận đấu, nhưng đó là một hoạt động tham lam, vì vậy điều này chỉ phù hợp với \wnhân vật áp chót trong chuỗi.

Lưu ý rằng bạn cần phải thoát khỏi parens và +.

sed 's/.*\(\w\).\+/\1/' myfile.txt
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.