Sử dụng lệnh tìm nhưng loại trừ các tệp trong hai thư mục


86

Tôi muốn tìm các tệp có đuôi _peaks.bednhưng loại trừ các tệp trong thư mục tmpscripts.

Lệnh của tôi như thế này:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Nhưng nó không hoạt động. Các tệp trong tmpscriptthư mục sẽ vẫn được hiển thị.

Có ai có ý tưởng về điều này?

Câu trả lời:


189

Đây là cách bạn có thể chỉ định điều đó với find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Giải trình:

  • find . - Bắt đầu tìm từ thư mục làm việc hiện tại (đệ quy theo mặc định)
  • -type f- Chỉ định findrằng bạn chỉ muốn các tệp trong kết quả
  • -name "*_peaks.bed" - Tìm kiếm các tệp có tên kết thúc bằng _peaks.bed
  • ! -path "./tmp/*" - Loại trừ tất cả các kết quả có đường dẫn bắt đầu bằng ./tmp/
  • ! -path "./scripts/*" - Đồng thời loại trừ tất cả các kết quả có đường dẫn bắt đầu bằng ./scripts/

Kiểm tra Giải pháp:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Bạn đã khá gần, -nametùy chọn chỉ xem xét tên cơ sở, trong đó -pathcoi như toàn bộ đường dẫn =)


Công việc tốt đẹp. Tuy nhiên, bạn đã quên một trong những điều OP muốn, đó là tìm các tệp có đuôi _peaks.bed.
alex

2
Điều này sử dụng một số tiện ích mở rộng trong GNU find, nhưng vì câu hỏi được gắn thẻ Linux nên đó không phải là vấn đề. Câu trả lời tốt.
Jonathan Leffler

1
Một lưu ý ngắn: nếu bạn sử dụng .lời nhắc tìm kiếm ban đầu, bạn phải sử dụng nó trong mỗi đường dẫn bạn loại trừ. Đối sánh đường dẫn khá nghiêm ngặt, nó không thực hiện tìm kiếm mờ. Vì vậy, nếu bạn sử dụng find / -type f -name *.bed" ! -path "./tmp/"nó sẽ không hoạt động. bạn cần phải ! -path "/tmp"làm cho nó hạnh phúc.
bóc vỏ

3
Điều quan trọng cần lưu ý là dấu * là quan trọng. $ ! -path "./directory/*"
Thomas Bennett

3
Theo các trang người đàn ông: "Để bỏ qua toàn bộ cây thư mục, hãy sử dụng -prunethay vì kiểm tra mọi tệp trong cây." Nếu các thư mục bị loại trừ của bạn chạy rất sâu hoặc có nhiều tệp và bạn quan tâm đến hiệu suất, thì hãy sử dụng -prunetùy chọn thay thế.
thdoan

8

Đây là một cách bạn có thể làm ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"

2
Điều này có ích lợi khi làm việc với bất kỳ phiên bản nào find, thay vì chỉ với GNU find. Tuy nhiên, câu hỏi được gắn thẻ Linux nên điều đó không quan trọng.
Jonathan Leffler

2

Sử dụng

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

hoặc là

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

hoặc là

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

Thứ tự là quan trọng. Nó đánh giá từ trái sang phải. Luôn bắt đầu với loại trừ đường dẫn.

Giải trình

Không sử dụng -not(hoặc !) để loại trừ toàn bộ thư mục. Sử dụng -prune. Như đã giải thích trong sách hướng dẫn:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

và trong sổ tay hướng dẫn tìm GNU:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

Thật vậy, nếu bạn sử dụng -not -path "./pathname", find sẽ đánh giá biểu thức cho mỗi nút bên dưới "./pathname".

biểu thức tìm chỉ là đánh giá điều kiện.

  • \( \)- hoạt động nhóm (bạn có thể sử dụng -path "./tmp" -prune -o -path "./scripts" -prune -o, nhưng nó dài dòng hơn).
  • -path "./script" -prune- nếu -pathtrả về true và là một thư mục, hãy trả về true cho thư mục đó và không đi xuống nó.
  • -path "./script" ! -prune- nó đánh giá là (-path "./script") AND (! -prune). Nó hoàn nguyên "luôn đúng" của tỉa thành luôn sai. Nó tránh in "./script"như một trận đấu.
  • -path "./script" -prune -false- vì -pruneluôn trả về true, bạn có thể theo dõi nó với -falseđể làm tương tự hơn !.
  • -o- Toán tử HOẶC. Nếu không có toán tử nào được chỉ định giữa hai biểu thức, nó sẽ mặc định là toán tử AND.

Do đó, \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printđược mở rộng thành:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

Bản in rất quan trọng ở đây vì nếu không có nó sẽ được mở rộng thành:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printđược thêm vào bằng cách tìm - đó là lý do tại sao hầu hết thời gian, bạn không cần phải thêm nó vào biểu thức của bạn. Và vì -prunetrả về true nên nó sẽ in ra "./script" và "./tmp".

Nó không cần thiết trong những cái khác vì chúng tôi đã chuyển sang -pruneluôn trả về false.

Gợi ý: Bạn có thể sử dụng find -D opt expr 2>&1 1>/dev/nullđể xem nó được tối ưu hóa và mở rộng như thế nào,
find -D search expr 2>&1 1>/dev/nullđể xem đường dẫn nào được kiểm tra.


0

Hãy thử một cái gì đó như

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

và đừng quá ngạc nhiên nếu tôi làm sai một chút. Nếu mục tiêu là một tệp thực thi (thay vì in), chỉ cần thay thế nó tại chỗ.


0

đối với tôi, giải pháp này không hoạt động trên trình thực thi lệnh với find, không thực sự biết tại sao, vì vậy giải pháp của tôi là

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Giải thích: giống như sampson-chen một với phần bổ sung của

-prune - bỏ qua đường dẫn thủ tục của ...

-o - Sau đó, nếu không khớp, hãy in kết quả, (lược bớt các thư mục và in kết quả còn lại)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz

Câu trả lời được chấp nhận không hoạt động, nhưng nó hoạt động. Sử dụng tỉa , find . -path ./scripts -prune -name '*_peaks.bed' -type f. Không chắc chắn làm thế nào để loại trừ nhiều thư mục. Điều này cũng liệt kê thư mục bị loại trừ cấp cao nhất mặc dù đã typeđược chỉ định. Loại trừ qua Grep có vẻ đơn giản hơn trừ khi bạn muốn sử dụng tính năng cắt tỉa để tăng tốc hoạt động tìm kiếm.
Mohnish

Tôi cũng gặp sự cố khi loại trừ nhiều thư mục, nhưng các nhận xét ở trên đã cho tôi câu trả lời hiệu quả. Tôi sử dụng nhiều trường hợp '-not -path' và trong mỗi biểu thức đường dẫn, tôi bao gồm tiền tố đầy đủ như được sử dụng trong tham số đầu tiên để 'tìm' và kết thúc mỗi trường hợp bằng dấu hoa thị (và thoát khỏi bất kỳ dấu chấm nào).
jetset

0

Bạn có thể thử bên dưới:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'

2
Đối với một câu hỏi cũ như vậy (4 năm!), Bạn muốn giải thích tại sao câu trả lời mới này tốt hơn hoặc khác biệt, chứ không chỉ là mã "kết xuất".
Nic3500
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.