Làm thế nào để chạy grep với nhiều mẫu AND?


86

Tôi muốn có được kết hợp nhiều mẫu với AND ẩn giữa các mẫu, nghĩa là tương đương với việc chạy một vài greps theo trình tự:

grep pattern1 | grep pattern2 | ...

Vậy làm thế nào để chuyển đổi nó thành một cái gì đó như thế nào?

grep pattern1 & pattern2 & pattern3

Tôi muốn sử dụng một grep đơn vì tôi đang xây dựng các đối số một cách linh hoạt, vì vậy mọi thứ phải khớp trong một chuỗi. Sử dụng bộ lọc là tính năng hệ thống, không phải grep, vì vậy nó không phải là một đối số cho nó.


Đừng nhầm lẫn câu hỏi này với:

grep "pattern1\|pattern2\|..."

Đây là một kết hợp đa mẫu OR .



Câu trả lời:


78

agrep có thể làm điều đó với cú pháp này:

agrep 'pattern1;pattern2'

Với GNU grep, khi được xây dựng với sự hỗ trợ của PCRE, bạn có thể làm:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

Với astgrep :

grep -X '.*pattern1.*&.*pattern2.*'

(thêm .*s dưới dạng <x>&<y>các chuỗi khớp với cả hai <x><y> chính xác , a&bsẽ không bao giờ khớp vì không có chuỗi nào có thể cả hai abcùng một lúc).

Nếu các mẫu không trùng nhau, bạn cũng có thể làm:

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

Cách di động tốt nhất có lẽ awklà như đã đề cập:

awk '/pattern1/ && /pattern2/'

Với sed:

sed -e '/pattern1/!d' -e '/pattern2/!d'

Xin lưu ý rằng tất cả những người sẽ có cú pháp biểu thức chính quy khác nhau.


1
Các agrepcú pháp không làm việc cho tôi ... phiên bản nào là nó được giới thiệu vào?
Raman

@Raman 2.04 từ năm 1992 đã có nó. Tôi không có lý do để tin rằng nó đã không ở đó từ đầu. Các phiên bản mới hơn (sau năm 1992) agrepcó thể được tìm thấy kèm theo glimpse / webglimpse . Có thể bạn có một cách thực hiện khác. Tôi đã có một lỗi cho phiên bản ast-grep, tuy nhiên, tùy chọn cho regexps tăng cường-Xkhông -A.
Stéphane Chazelas

@ StéphaneChazelas Cảm ơn, tôi có agrep0.8.0 trên Fedora 23. Điều này dường như khác agrepvới cái bạn tham khảo.
Raman

1
@Raman, âm thanh của bạn giống như TREagrep .
Stéphane Chazelas

2
@Techiee, hoặc chỉawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas

19

Bạn đã không chỉ định phiên bản grep, điều này rất quan trọng. Một số công cụ regrec cho phép nhiều kết hợp khớp được nhóm bởi AND bằng cách sử dụng '&' nhưng đây là tính năng không chuẩn và không di động. Nhưng, ít nhất GNU grep không hỗ trợ điều này.

OTOH bạn chỉ có thể thay thế grep bằng sed, awk, perl, v.v. (được liệt kê theo thứ tự tăng cân). Với awk, lệnh sẽ giống như

awk '/ regapi1 / && / regapi2 / && / regexp3 / {print; } '

và nó có thể được xây dựng để được chỉ định trong dòng lệnh một cách dễ dàng.


3
Chỉ cần nhớ rằng awksử dụng ERE, ví dụ như tương đương với grep -E, trái ngược với BRE grepsử dụng đơn giản .
jw013

3
awkCác biểu thức chính thức được gọi là ERE, nhưng trên thực tế, chúng có một chút bình dị. Đây có lẽ là chi tiết hơn bất cứ ai quan tâm: wiki.alpinelinux.org/wiki/Regex
dubiousjim

Cảm ơn bạn, grep 2.7.3 (openSUSE). Tôi ủng hộ bạn, nhưng tôi sẽ tiếp tục mở câu hỏi trong một thời gian, có thể có một số mẹo cho grep (không phải là tôi không thích awk- chỉ đơn giản là biết nhiều hơn là tốt hơn).
greenoldman

2
Hành động mặc định là in dòng phù hợp để { print; }phần không thực sự cần thiết hoặc hữu ích ở đây.
tripleee

7

Nếu patternschứa một mẫu trên mỗi dòng, bạn có thể làm một cái gì đó như thế này:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

Hoặc điều này khớp với các chuỗi thay vì các biểu thức thông thường:

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

Để in tất cả thay vì không có dòng đầu vào trong trường hợp đó patternslà sản phẩm nào, thay thế NR==FNRvới FILENAME==ARGV[1], hoặc có ARGIND==1trong gawk.

Các hàm này in các dòng STDIN chứa mỗi chuỗi được chỉ định làm đối số dưới dạng chuỗi con. galà viết tắt của grep all và gaibỏ qua trường hợp.

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }

7

Đây không phải là một giải pháp tốt nhưng minh họa một "mánh khóe" hơi ngầu

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont

1
Sử dụng một trong hai chained-grep()hoặc function chained-grepkhông function chained-grep(): unix.stackexchange.com/questions/73750/ trên
nisetama

3

git grep

Đây là cú pháp sử dụng git grepkết hợp nhiều mẫu bằng biểu thức Boolean :

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

Lệnh trên sẽ in các dòng khớp với tất cả các mẫu cùng một lúc.

--no-index Tìm kiếm tệp trong thư mục hiện tại không được quản lý bởi Git.

Kiểm tra man git-grepgiúp đỡ.

Xem thêm:

Đối với hoạt động OR , xem:


1

ripgrep

Dưới đây là ví dụ sử dụng rg:

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

Đây là một trong những công cụ grepping nhanh nhất, vì nó được xây dựng trên công cụ regex của Rust , sử dụng automata hữu hạn, SIMD và tối ưu hóa theo nghĩa đen tích cực để giúp tìm kiếm rất nhanh.

Xem thêm yêu cầu tính năng liên quan tại GH-875 .


1

Đây là của tôi, và điều này hoạt động cho các từ trong nhiều dòng:

Sử dụng find . -type ftheo sau là nhiều
-exec grep -q 'first_word' {} \;
và từ khóa cuối cùng với
-exec grep -l 'nth_word' {} \;

-q
-ltập tin hiển thị yên tĩnh / im lặng với các trận đấu

Danh sách trả về sau đây của tên tệp có chữ 'thỏ' và 'lỗ' trong đó:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;


-2

Để tìm TẤT CẢ các từ (hoặc mẫu), bạn có thể chạy grep trong vòng lặp FOR . Ưu điểm chính ở đây, là tìm kiếm từ một danh sách các biểu thức chính quy .

EDIT câu trả lời của tôi với một ví dụ thực tế:

# search_all_regex_and_error_if_missing.sh 

find_list="\
^a+$ \
^b+$ \
^h+$ \
^d+$ \
"

for item in $find_list; do
   if grep -E "$item" file_to_search_within.txt 
   then
       echo "$item found in file."
   else
       echo "Error: $item not found in file. Exiting!"
       exit 1
   fi
done

Bây giờ hãy chạy nó trên tập tin này:

hhhhhhhhhh

aaaaaaa

bbbbbbbbbb

ababbabaabbaaa

ccccccc

DSFdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ được tìm thấy trong tập tin.

bbbbbbbbbbbbbb

^ b + $ được tìm thấy trong tập tin.

hhhhhhhhhh

^ h + $ được tìm thấy trong tập tin.

Lỗi: ^ d + $ không tìm thấy trong tệp. Thoát!


1
Logic của bạn bị lỗi - Tôi đã yêu cầu ALLtoán tử, mã của bạn hoạt động như ORtoán tử, không phải AND. Và btw. cho rằng ( OR) là giải pháp dễ dàng hơn nhiều được đưa ra ngay trong câu hỏi.
greenoldman

@greenoldman Logic rất đơn giản: Vòng lặp for sẽ lặp lại trên TẤT CẢ các từ / mẫu trong danh sách và nếu nó được tìm thấy trong tệp - sẽ in nó. Vì vậy, chỉ cần loại bỏ những thứ khác nếu bạn không cần hành động trong trường hợp không tìm thấy từ.
Noam Manos

1
Tôi hiểu logic của bạn cũng như câu hỏi của tôi - Tôi đã hỏi về ANDtoán tử, có nghĩa là tệp chỉ là một điểm nhấn tích cực nếu nó khớp với mẫu A và mẫu B và mẫu C và ... ANDTrong trường hợp của bạn, tệp đó là tích cực nếu nó phù hợp mẫu A hoặc mẫu B hoặc ... Bạn có thấy sự khác biệt bây giờ không?
greenoldman

@greenoldman không chắc chắn lý do tại sao bạn nghĩ rằng vòng lặp này không kiểm tra điều kiện AND cho tất cả các mẫu? Vì vậy, tôi đã chỉnh sửa câu trả lời của mình bằng một ví dụ thực tế: Nó sẽ tìm kiếm trong tệp cho tất cả regex của danh sách, và trên cái đầu tiên bị thiếu - sẽ thoát với lỗi.
Noam Manos

Bạn có nó ngay trước mắt, bạn có trận đấu tích cực ngay sau khi trận đấu đầu tiên được thực hiện. Bạn nên "thu thập" tất cả các kết quả và tính toán ANDtrên chúng. Sau đó, bạn nên viết lại tập lệnh để chạy trên nhiều tệp - sau đó có thể bạn nhận ra rằng câu hỏi đã được trả lời và nỗ lực của bạn không mang lại điều gì cho bảng, xin lỗi.
greenoldman
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.