Làm thế nào để tìm kiếm tập tin trong đó tồn tại hai từ khác nhau?


14

Tôi đang tìm cách để tìm kiếm các tệp trong đó có hai trường hợp từ tồn tại trong cùng một tệp. Tôi đã sử dụng những điều sau đây để thực hiện các tìm kiếm của mình cho đến thời điểm này:

find . -exec grep -l "FIND ME" {} \;

Vấn đề tôi gặp phải là nếu không có chính xác một khoảng trống giữa "TÌM" và "TÔI", kết quả tìm kiếm không mang lại tệp. Làm cách nào để điều chỉnh chuỗi tìm kiếm cũ trong đó cả hai từ "TÌM" và "ME tồn tại trong một tệp trái ngược với" TÌM TÔI "?

Tôi đang sử dụng AIX.


1
Các từ tồn tại ở bất cứ đâu trong tệp, hoặc chúng luôn nằm trên cùng một dòng?
Sobrique 21/07/2015

Ý định là cùng một dòng.
Chad Harrison

Một cách khác, nếu các từ nằm trên cùng một dòng là sử dụng biểu thức chính quy với grep -E/ egrepmô tả tất cả các mẫu bạn quan tâm (và sử dụng +thay vì ;nếu tìm thấy của bạn có hỗ trợ +.
MattBianco

Câu trả lời:


21

Với các công cụ GNU:

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

Bạn có thể làm chuẩn:

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

Nhưng điều đó sẽ chạy hai greps mỗi tập tin. Để tránh chạy nhiều greps mà vẫn có thể di động trong khi vẫn cho phép bất kỳ ký tự nào trong tên tệp, bạn có thể làm:

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

Ý tưởng là chuyển đổi đầu ra findthành định dạng phù hợp với xargs (mong đợi một khoảng trống (SPC / TAB / NL và các khoảng trống khác từ ngôn ngữ của bạn với một số cách triển khai xargs) danh sách các từ có thể trích dẫn đơn, dấu ngoặc kép và dấu gạch chéo ngược thoát khoảng trống và nhau).

Nói chung, bạn không thể xử lý hậu kỳ của đầu ra find -print, bởi vì nó phân tách các tên tệp bằng một ký tự dòng mới và không thoát khỏi các ký tự dòng mới được tìm thấy trong tên tệp. Chẳng hạn, nếu chúng ta thấy:

./a
./b

Chúng tôi không có cách nào để biết liệu đó là một tệp được gọi btrong một thư mục được gọi a<NL>.hay nếu đó là hai tệp ab .

Bằng cách sử dụng .//., vì //không thể xuất hiện theo cách khác trong đường dẫn tệp dưới dạng đầu ra find(vì không có thư mục nào có tên trống và /không được phép trong tên tệp), chúng tôi biết rằng nếu chúng tôi thấy một dòng có chứa //, thì đó là dòng đầu tiên của một tên tệp mới. Vì vậy, chúng ta có thể sử dụng nóawk lệnh để thoát tất cả các ký tự dòng mới nhưng những ký tự đi trước các dòng đó.

Nếu chúng ta lấy ví dụ ở trên, findsẽ xuất ra trong trường hợp đầu tiên (một tệp):

.//a
./b

Mà awk thoát đến:

.//a\
./b

Vì vậy, xargsxem nó như là một đối số. Và trong trường hợp thứ hai (hai tệp):

.//a
.//b

awksẽ để lại như vậy, vì vậy xargsnhìn thấy hai đối số.


Tại sao không sử dụng find ... -print0grep --nullthay vào đó?
đánh bại

@razzed, không chắc ý của bạn là gì. grep --null(aka -Z) được sử dụng trong phần đầu tiên nhưng là phần mở rộng GNU. -print0(một phần mở rộng GNU khác) sẽ không giúp đỡ ở đây.
Stéphane Chazelas

Cảm ơn. Tôi muốn bọc mã shell của bạn thành một tập lệnh lấy thư mục tìm kiếm làm đối số từ dòng lệnh. Tôi không chắc chắn điều gì .//.có nghĩa là gì , và tự hỏi làm thế nào tôi có thể sửa đổi điều đó để chấp nhận một đối số từ dòng lệnh, nói $1gì?
Tim

Cảm ơn. Trong lệnh của bạn, có cần thiết phải sử dụng -print0với find-0với xargs?
Tim

@Tim, không chắc ý bạn là gì. Tôi không sử dụng find -print0bất cứ nơi nào trong câu trả lời của tôi.
Stéphane Chazelas

8

Nếu các tập tin trong một thư mục duy nhất và tên của họ không chứa không gian, tab, xuống dòng, *, ?cũng không [ký tự và không bắt đầu với -cũng không ., này sẽ nhận được một danh sách các tập tin có chứa ME, sau đó thu hẹp lại để những người mà cũng chứa TÌM.

grep -l FIND `grep -l ME *`

NÀY cần thêm upvote !! Thanh lịch hơn nhiều so với câu trả lời "được chấp nhận". Đã làm cho tôi.
roblogic

Vừa làm grep -l CategoryLinearAxis `grep -l labelJsFunction *`trong khi tìm kiếm các tập tin có cả hai thuộc tính trong đó. Thật là một cách hoàn hảo để làm điều đó. +1
WEBjuju

3

Với awkbạn cũng có thể chạy:

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

Nó sử dụng cxcyđể tính cho phù hợp với dòng FINDvà tương ứng ME. Trong ENDkhối, nếu cả hai bộ đếm> 0, nó sẽ in FILENAME.
Điều này sẽ nhanh hơn / hiệu quả hơn với gnu awk:

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +

2

Hoặc sử dụng egrep -ehoặc grep -Enhư thế này:

find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

hoặc là

find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

Việc +tìm kiếm (nếu được hỗ trợ) thêm nhiều tên tệp (đường dẫn) làm đối số cho lệnh đang được chỉnh sửa -exec. Điều này giúp tiết kiệm các quy trình và nhanh hơn rất nhiều so với \;việc gọi lệnh một lần cho mỗi tệp được tìm thấy.

-type f chỉ khớp với các tập tin, để tránh grepping trên một thư mục.

'(ME.*FIND|FIND.*ME)'là một biểu thức chính quy khớp với bất kỳ dòng nào chứa "ME" theo sau là "TÌM" hoặc "TÌM" theo sau là "ME". (trích dẫn đơn để ngăn shell diễn giải các ký tự đặc biệt).

Thêm một -ivàogrep lệnh để làm cho nó case-insensitive.

Để chỉ khớp các dòng có "TÌM" xuất hiện trước "ME", hãy sử dụng 'FIND.*ME' .

Để yêu cầu khoảng trắng (1 hoặc nhiều hơn, nhưng không có gì khác) giữa các từ: 'FIND +ME'

Để cho phép khoảng trắng (0 trở lên, nhưng không có gì khác) giữa các từ: 'FIND *ME'

Các kết hợp là vô tận với các biểu thức thông thường và miễn là bạn chỉ quan tâm đến việc kết hợp trên cơ sở hàng liên tục, egrep rất mạnh mẽ.


Có phải hầu hết các greps không hỗ trợ "-r"? Điều đó sẽ loại bỏ "tìm", nhưng có thể có ổ cắm hoặc các tệp không đơn giản khác trong cây đang được tìm kiếm.
đánh cắp

OP sử dụng AIX và có findtrong câu hỏi.
MattBianco

0

Nhìn vào câu trả lời được chấp nhận, nó có vẻ phức tạp hơn mức cần thiết. Các phiên bản GNU findgrepxargshỗ trợ các chuỗi kết thúc NULL. Nó đơn giản như:

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

Bạn có thể sửa đổi findlệnh của mình để lọc thành các tệp bạn muốn và nó hoạt động với tên tệp chứa bất kỳ ký tự nào; mà không cần thêm sự phức tạp của sedphân tích cú pháp. Nếu bạn muốn xử lý thêm các tệp, hãy thêm tệp khác --nullvào tệp cuối cùnggrep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

Và, như một chức năng:

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

Rõ ràng, sử dụng câu trả lời được chấp nhận nếu bạn không chạy các phiên bản GNU của các công cụ này.


1
--null, --print0, -0Là tất cả các phần mở rộng GNU. Mặc dù một số trong số chúng được tìm thấy trong các triển khai khác hiện nay, chúng vẫn không thể di động và không theo tiêu chuẩn POSIX hoặc Unix.
Stéphane Chazelas
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.