it-swarm-vi.com

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

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 giải thích | như một đường ống và phàn nàn khi bar khô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?

681
Dan

Đầu tiên, bạn cần bảo vệ mô hình khỏi sự mở rộng của Shell. 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

(cũng lưu ý -- end-of-tùy chọn-đánh dấu để dừng một số triển khai grep bao gồm GNU grep khỏi việc xử lý tệp được gọi -foo-.txt chẳng hạn (sẽ được Shell mở rộng từ *.txt) thành một tùy chọn (ngay cả khi nó tuân theo một đối số không phải tùy chọn ở đây)).

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 bằng chữ, trích dẫn bằng chữ, mở chuỗi bằng chữ).

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

Thứ hai, grep hỗ trợ ít nhất 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, biểu thức chính quy mở rộng . Bạn cần chuyển tùy chọn -E cho grep để chọn nó (trước đây đã được thực hiện với lệnh egrep riêng biệt²)

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 tùy chọn -e.

grep -e foo -e bar -- *.txt

Hoặc đặt các mẫu trên một số dòng:

grep -- 'foo
bar' *.txt

Hoặc lưu trữ các mẫu đó trong một tệp, mỗi mẫu trên một dòng và chạy

grep -f that-file -- *.txt

Lưu ý rằng nếu *.txt mở rộng thành một tệp duy nhất, grep sẽ không có tiền tố khớp với các tên giống như khi có nhiều hơn một tệp. Để giải quyết vấn đề đó, với một số triển khai grep như GNU grep, bạn có thể sử dụng tùy chọn -H hoặc với bất kỳ triển khai nào, bạn có thể vượt qua /dev/null như một đối số phụ.


¹ một số triển khai grep thậm chí còn hỗ trợ nhiều hơn như các triển khai tương thích với Perl với -P hoặc tăng cường với -X, -K cho ký tự đại diện ksh ...

² trong khi egrep đã bị POSIX phản đối và đôi khi không còn được tìm thấy trên một số hệ thống, trên một số hệ thống khác như Solaris khi các tiện ích POSIX hoặc GNU chưa được cài đặt, sau đó egrep là tùy chọn duy nhất của bạn vì /bin/grep không hỗ trợ -e, -f, -E, \| hoặc các mẫu nhiều dòng

929
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. :)

109
user unknown

Giống như TC1 đã nói, -F dườ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

Đầ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, grep sẽ không hiểu trực tiếp sự thay thế; bạn sẽ cần sử dụng egrep hoặc (với GNU grep only) 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 quy lớn hơn.)

17
geekosaur

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 fgrep hoặc grep -F Vớ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.

8
Moustafa Elqabbany

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

egrep 'rose.*Lotus|lotus.*rose' some_file
6
Abhishek

Ống (|) là một nhân vậ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 để xóa nghĩa đặc biệt của các ký tự hoặc từ nhất định đối với Shell. 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ố.

Việc đó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à nhân vật thoát.

Xem: Những nhân vật nào cần được 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, hãy xem: Cách 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: Cách 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?

4
kenorb

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
3
DHDHDHD

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
3
tsmets

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 dấu ngoặc đơn hoặc đường ống, tức là, grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='

2
jeremysprofile

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

[email protected]:/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 [email protected]:/home/sshuser#
1
Mansur Ali

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ó .txt trong tên của họ.
[.___.] 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!!

1
Bhagyesh Dudhediya

để 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:]]"

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

1
Fuseteam