Làm cách nào để grep cho nhiều mẫu có mẫu có ký tự ống?


624

Tôi muốn tìm tất cả các dòng trong một số tệp khớp với một trong hai mẫu. Tôi đã cố gắng tìm các mẫu tôi đang tìm kiếm bằng cách gõ

grep (foo|bar) *.txt

nhưng shell diễn giải |như một đường ống và phàn nàn khi barkhông thể thực thi được.

Làm cách nào tôi có thể grep cho nhiều mẫu trong cùng một tập tin?



grep 'word1 \ | word2 \ | word3' / path / to / file
lambodar

Câu trả lời:


861

Đầu tiên, bạn cần bảo vệ mô hình khỏi sự mở rộng của vỏ. Cách dễ nhất để làm điều đó là đặt dấu ngoặc đơn xung quanh nó. Dấu ngoặc đơn ngăn chặn sự mở rộng của bất cứ điều gì giữa chúng (bao gồm dấu gạch chéo ngược); điều duy nhất bạn không thể làm sau đó là có dấu ngoặc đơn trong mẫu.

grep 'foo*' *.txt

Nếu bạn cần một trích dẫn duy nhất, bạn có thể viết nó dưới dạng '\''(kết thúc chuỗi ký tự, trích dẫn bằng chữ, chuỗi ký tự mở).

grep 'foo*'\''bar' *.txt

Thứ hai, grep hỗ trợ hai cú pháp cho các mẫu. Cú pháp mặc định cũ ( biểu thức chính quy cơ bản ) không hỗ trợ |toán tử xen kẽ ( ), mặc dù một số phiên bản có phần mở rộng, nhưng được viết bằng dấu gạch chéo ngược.

grep 'foo\|bar' *.txt

Cách di động là sử dụng cú pháp mới hơn, mở rộng các biểu thức chính quy . Bạn cần phải vượt qua -Etùy chọn grepđể chọn nó. Trên Linux, bạn cũng có thể nhập egrepthay vì grep -E(trên các thông báo khác, bạn có thể đặt bí danh đó).

grep -E 'foo|bar' *.txt

Một khả năng khác khi bạn chỉ tìm kiếm bất kỳ mẫu nào trong số các mẫu (trái ngược với việc xây dựng một mẫu phức tạp bằng cách sử dụng hàm phân tách) là chuyển nhiều mẫu cho grep. Bạn có thể làm điều này bằng cách đi trước mỗi mẫu với -etùy chọn.

grep -e foo -e bar *.txt

18
Là một sidenote - khi các mẫu được cố định, bạn thực sự nên tập thói quen fgrephoặc grep -F, đối với các mẫu nhỏ, sự khác biệt sẽ không đáng kể nhưng khi chúng dài hơn, các lợi ích bắt đầu hiển thị ...
TC1

7
@ TC1 fgrep không được chấp nhận theo trang man
ramn

18
@ TC1 Việc grep -Fcó lợi ích hiệu suất thực tế hay không phụ thuộc vào việc triển khai grep: dù sao thì một số trong số chúng cũng áp dụng cùng một thuật toán, do đó, điều này chỉ -Ftạo ra sự khác biệt đối với thời gian phân tích mẫu và không theo thời gian tìm kiếm. GNU grep không nhanh hơn với -F, ví dụ (nó cũng có một lỗi khiến grep -Fchậm hơn trong các địa điểm đa bào - cùng một mẫu không đổi với grepthực sự nhanh hơn đáng kể!). Mặt khác, BusyBox grep có lợi rất nhiều từ -Fcác tệp lớn.
Gilles

4
Có lẽ nên đề cập rằng đối với các mẫu phức tạp hơn, trong đó sự xen kẽ chỉ dành cho một phần của biểu thức chính quy, nó có thể được nhóm với "\ (" và "\)" (lối thoát dành cho "biểu thức chính quy cơ bản" mặc định ) (?).
Peter Mortensen

4
Lưu ý rằng egrepcó trước grep -E. Nó không phải là GNU cụ thể (chắc chắn nó không liên quan gì đến Linux). Trên thực tế, bạn vẫn sẽ tìm thấy các hệ thống như Solaris nơi mặc định grepvẫn không hỗ trợ -E.
Stéphane Chazelas

90
egrep "foo|bar" *.txt

hoặc là

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

chọn lọc trích dẫn trang man của gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

Lúc đầu tôi không đọc thêm, vì vậy tôi không nhận ra sự khác biệt tinh tế:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

Tôi luôn luôn sử dụng egrep và parens không cần thiết, bởi vì tôi đã học được từ các ví dụ. Bây giờ tôi đã học được một cái gì đó mới. :)


22

Giống như TC1 đã nói, -Fdường như là tùy chọn có thể sử dụng:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

1
@poige Tôi không biết về tùy chọn $ 'foo \ nbar', không chắc cách mở rộng hoạt động ở đây, cần phải tra cứu, nhưng cảm ơn bạn, điều đó thực sự hữu ích.
haridsv

Đẹp! Tùy chọn này dường như cũng làm cho nó chạy nhanh hơn nhiều (vì nó vô hiệu hóa regex).
qwertzguy

15

Đầu tiên, bạn cần sử dụng dấu ngoặc kép cho các ký tự đặc biệt. Thứ hai, ngay cả như vậy, grepsẽ không hiểu thay thế trực tiếp; bạn sẽ cần sử dụng egrephoặc (chỉ với GNU grep) grep -E.

egrep 'foo|bar' *.txt

(Các dấu ngoặc đơn là không cần thiết trừ khi sự thay thế là một phần của biểu thức chính lớn hơn.)


4
Trên thực tế, grep -Elà tiêu chuẩn hơn egrep.
jw013

8

Nếu bạn không cần biểu thức chính quy, sẽ nhanh hơn nhiều khi sử dụng fgrephoặc grep -Fvới nhiều tham số -e, như thế này:

fgrep -efoo -ebar *.txt

fgrep(cách khác grep -F) nhanh hơn nhiều so với grep thông thường vì nó tìm kiếm các chuỗi cố định thay vì các biểu thức thông thường.


4
Xin vui lòng xem thêm các ý kiến ​​trên trang này đề cập đến việc fgrepkhông được chấp nhận.
phk

6

Bạn có thể thử lệnh dưới đây để có kết quả:

egrep 'rose.*lotus|lotus.*rose' some_file

3

Một cách rẻ tiền và vui vẻ để grep cho nhiều mẫu:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq

Nó có thể được hưởng lợi từ một lời giải thích.
Peter Mortensen

2
Giải thích là -ftùy chọn của grep lấy một tệp có nhiều mẫu. Thay vì tạo một tệp tạm thời (mà bạn có thể quên xóa sau đó), chỉ cần sử dụng thay thế quy trình của trình bao:grep -f <(echo foo; echo bar) *.txt
Jakob

3

Tube ( |) là một ký tự shell đặc biệt, do đó, nó cần phải được thoát ( \|) hoặc được trích dẫn theo hướng dẫn sử dụng ( man bash):

Trích dẫn được sử dụng để loại bỏ ý nghĩa đặc biệt của các ký tự hoặc từ nhất định vào vỏ. Nó có thể được sử dụng để vô hiệu hóa xử lý đặc biệt cho các ký tự đặc biệt, để ngăn các từ dành riêng không được nhận dạng như vậy và để ngăn chặn việc mở rộng tham số.

Đóng dấu các ký tự trong dấu ngoặc kép sẽ giữ giá trị bằng chữ của tất cả các ký tự trong dấu ngoặc kép

Dấu gạch chéo ngược không trích dẫn ( \) là ký tự thoát.

Xem: Những nhân vật nào cần phải trốn thoát trong Bash?

Dưới đây là một vài ví dụ (sử dụng các công cụ chưa được đề cập):

  • Sử dụng ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Sử dụng git grep:

    • git grep --no-index -e foo --or -e bar

      Lưu ý: Nó cũng hỗ trợ các biểu thức Boolean như --and, --or--not.

Đối với hoạt động AND trên mỗi dòng, xem: Làm thế nào để chạy grep với nhiều mẫu AND?

Đối với hoạt động AND trên mỗi tệp, hãy xem: Làm thế nào để kiểm tra tất cả nhiều chuỗi hoặc biểu thức chính tồn tại trong một tệp?


3

Tôi đã có nhật ký truy cập trong đó ngày được định dạng một cách ngu ngốc: [30 / tháng 6/2013: 08: 00: 45 +0200]

Nhưng tôi cần hiển thị nó là: 30 tháng 6/2013 08:00:45

Vấn đề là khi sử dụng "HOẶC" trong câu lệnh grep của tôi, tôi đã nhận được hai biểu thức khớp trên hai dòng riêng biệt.

Đây là giải pháp:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log

2

TL; DR: nếu bạn muốn làm nhiều thứ hơn sau khi khớp một trong nhiều mẫu, hãy đính kèm chúng như trong \(pattern1\|pattern2\)

Ví dụ: Tôi muốn tìm tất cả các vị trí nơi một biến chứa tên 'ngày' được xác định là Chuỗi hoặc int. (ví dụ: "int cronDate =" hoặc "Chuỗi textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

Với grep -E, bạn không cần phải thoát khỏi dấu ngoặc đơn hoặc đường ống, nghĩa là,grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

Điều này làm việc cho tôi

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#

1

Có nhiều cách để làm điều này.

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

Tùy chọn thứ 3 và thứ 4 sẽ chỉ grep trong các tệp và tránh các thư mục có .txttên của chúng.
Vì vậy, theo trường hợp sử dụng của bạn, bạn có thể sử dụng bất kỳ tùy chọn nào được đề cập ở trên.
Cảm ơn!!


0

để thêm vào câu trả lời của @ geekizard , nếu bạn có nhiều mẫu cũng chứa các tab và khoảng trắng , bạn sử dụng lệnh sau

grep -E "foo[[:blank:]]|bar[[:blank:]]"

trong đó [[:blank:]]lớp ký tự RE đại diện cho một khoảng trắng hoặc ký tự tab

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.