Chạy `grep` không bao gồm tệp trong một đường dẫn cụ thể


12

Tôi muốn loại trừ các tập tin ./test/main.cpptừ tìm kiếm của tôi.

Đây là những gì tôi đang thấy:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Tôi biết có thể có được đầu ra mà tôi muốn bằng cách sử dụng nhiều lệnh trong sắp xếp ống và bộ lọc, nhưng có một số trích dẫn / thoát sẽ giúp greptôi hiểu được những gì tôi muốn?


Một giải pháp dựa trên việc lọc đầu ra không có tỷ lệ tốt vì nó không cần tìm kiếm tệp trước khi loại trừ các kết quả liên quan. Vấn đề được phóng to nếu tôi muốn loại trừ toàn bộ thư mục (với --exclude-dir). Đó là lý do tại sao tôi muốn làm cho grep thực hiện loại trừ nguyên bản.
tộc

1
--exclude chỉ định toàn cầu không phải là một con đường
tiếng Ba Tư

Câu trả lời:


6

grep không thể làm điều này cho tệp trong một thư mục nhất định nếu bạn có nhiều tệp có cùng tên trong các thư mục khác nhau, hãy sử dụng find:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


Tại sao bạn thoát \!\+? Nó dường như hoạt động tốt mà không có dấu gạch chéo ngược.
tộc

@nobar Tôi đã quen với nó bởi vì một số ký tự là từ khóa shell nên bạn sẽ không bao giờ ngạc nhiên vì không có gì có thể xảy ra nếu chúng được thoát.
MichalH

" grepKhông thể làm điều này, sử dụng findthay thế" - hoàn hảo.
tộc

4

Tôi không nghĩ rằng nó có thể với GNU grep. Bạn không cần đường ống mặc dù.

Với find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

Với zsh:

grep pattern ./**/*~./test/main.cpp(.)

(không bao gồm các tệp ẩn, cũng như để loại trừ .git, .svn ...).


2

Tôi có thể viết một cuốn sách: "Nghệ thuật đã mất xargs". Việc find ... -exec … ';khởi chạy một grep cho mỗi tệp (nhưng biến thể -exec … +không có). Chà, chúng ta đang lãng phí chu kỳ CPU trong những ngày này, vậy tại sao không, phải không? Nhưng nếu hiệu suất và bộ nhớ và sức mạnh là một vấn đề: sử dụng xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU của find's -print0sẽ NUL-terminate đầu ra và nó xargs' -0tùy chọn định dạng mà tôn vinh như là đầu vào. Điều này đảm bảo bất kỳ ký tự vui nhộn nào mà tệp của bạn có, đường dẫn sẽ không bị lẫn lộn. Các -rtùy chọn đảm bảo không có lỗi trong trường hợp findphát hiện không có gì.

Lưu ý, bây giờ bạn có thể làm những việc như:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep's -zlàm điều tương tự như xargs ' -0.


3
Một số lưu ý thú vị, nhưng tôi không chắc bạn đã đúng về vấn đề hiệu suất. Theo tôi hiểu nó find -exec (cmd) {} +hoạt động giống như xargsfind -exec (cmd) {} \;hoạt động giống như xargs -n1. Nói cách khác, tuyên bố của bạn chỉ đúng nếu \;phiên bản được sử dụng.
tộc

3
Đường ống vào xargsít hiệu quả hơn so với sử dụng -exec … +(mặc dù không đáng kể). Không có câu trả lời ở đây thậm chí đề cập đến -exec … \;.
Gilles 'SO- ngừng trở nên xấu xa'

1
Vâng, s - t. Tôi hẹn hò với chính mình. Cảm ơn các ý kiến ​​và sửa chữa. Tôi nghĩ rằng \ + là một lỗi đánh máy. Hãy nhìn xem, -exec ... +được thêm vào tháng 1 năm 2005. Vâng, tôi không lỗi thời ... tại ... tất cả.
Otheus

2

Nếu các findhỗ trợ của bạn -pathđã được thêm vào POSIX vào năm 2008 nhưng vẫn còn thiếu trong Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
Tôi không nghĩ rằng nó sẽ hoạt động vì quý tộc muốn main.cpp trong các thư mục khác
Eric Renouf

1
mẫu của bạn sẽ không loại trừ main.cpp khỏi tất cả các thư mục khác chứ? Điều đó sẽ không được mong muốn
Eric Renouf

@EricRenouf: Ồ, lỗi của tôi, đọc sai. Cập nhật câu trả lời của tôi.
cuonglm

@Gilles: Tại sao -pathkhông phải là POSIX?
cuonglm

À, xin lỗi, lỗi của tôi, nó đã được thêm vào năm 2008. Mặc dù vậy vẫn còn thiếu từ Solaris.
Gilles 'SO- ngừng trở nên xấu xa'

1

Đối với hồ sơ, đây là cách tiếp cận mà tôi thích:

grep pattern $(find . -type f ! -path './test/main.cpp')

Bằng cách giữ nguyên grepở đầu lệnh, tôi nghĩ rằng điều này rõ ràng hơn một chút - cộng với việc nó không tắt tính năng greptô sáng màu. Theo một nghĩa nào đó, sử dụng findthay thế lệnh chỉ là một cách để mở rộng / thay thế tập hợp tìm kiếm tệp (giới hạn) grepcủa chức năng.


Đối với tôi, find -execcú pháp là loại phức tạp. Một phức tạp vớifind -exec là (đôi khi) cần phải thoát khỏi các ký tự khác nhau (đáng chú ý là nếu \;được sử dụng theo Bash). Chỉ với mục đích đưa mọi thứ vào bối cảnh quen thuộc, hai lệnh sau về cơ bản là tương đương:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Nếu bạn muốn loại trừ các thư mục con , có thể cần phải sử dụng ký tự đại diện. Tôi không hiểu đầy đủ về lược đồ ở đây - nói về phức tạp :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Một lưu ý nữa để khái quát hóa findcác giải pháp dựa trên sử dụng trong các tập lệnh : Dòng greplệnh nên bao gồm -H/--with-filename tùy chọn . Nếu không, nó sẽ thay đổi định dạng đầu ra trong trường hợp chỉ có một tên tệp trong kết quả tìm kiếm từ đó find. Điều này là đáng chú ý bởi vì nó dường như không cần thiết nếu sử dụng greptìm kiếm tệp gốc (với -rtùy chọn).

... Mặc dù tốt hơn, là bao gồm /dev/null như một tệp đầu tiên để tìm kiếm. Điều này giải quyết hai vấn đề:

  • Nó đảm bảo rằng nếu có một tệp để tìm kiếm, grepsẽ nghĩ có hai tệp và sử dụng chế độ đầu ra nhiều tệp.
  • Nó đảm bảo rằng nếu không có tệp để tìm kiếm, grep sẽ nghĩ rằng có một tệp và không bị treo chờ trên stdin.

Vì vậy, câu trả lời cuối cùng là:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

Bạn không nên sử dụng đầu ra của findmột thay thế lệnh. Điều này phá vỡ nếu có tên tệp chứa dấu cách hoặc ký tự đặc biệt khác. Sử dụng find -exec, nó mạnh mẽ và dễ sử dụng.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: Điểm rất tốt - cũng là đầu ra có thể vượt quá giới hạn kích thước dòng lệnh của một số chương trình. Emptor caveat.
tộc

Ừ Cú pháp 'tìm' rất khó khăn. '-o' là một toán tử "hoặc" (cũng là '-hoặc' trên Linux), nhưng đó là cách sử dụng thông thường (ví dụ với '-prune') không ánh xạ khái niệm theo khái niệm logic hay. Đó là một chức năng hoặc hơn là logic hoặc.
tộc

Một cách khác để loại trừ các thư mục con dựa trên việc khớp tên : find -iname "*target*" -or -name 'exclude' -prune. Vâng, đó là loại công việc - thư mục được cắt tỉa sẽ được liệt kê, nhưng không được tìm kiếm. Nếu bạn không muốn nó được liệt kê, bạn có thể nối thêm một loại dự phòng! -name 'exclude'
quý
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.