Làm thế nào để loại trừ một thư mục trong find. chỉ huy


1380

Tôi đang cố chạy một findlệnh cho tất cả các tệp JavaScript, nhưng làm cách nào để loại trừ một thư mục cụ thể?

Đây là findmã chúng tôi đang sử dụng.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done

10
Thư mục bạn cần loại trừ là gì?
Archetypal Paul

11
Nó tốt hơn để sử dụng find ... | while read -r file .... Ngoài ra, tốt hơn là chấp nhận và nâng cao câu trả lời.
Tạm dừng cho đến khi có thông báo mới.

trong khi đọc chậm, để vào nhanh hơn
mpapis

18
@mpapis trong khi đọc chính xác xử lý các dòng đầy đủ với khoảng trắng.
Jean-Philippe Pellet

1
Chỉ cần chạy nó trong một thư mục với các tệp có khoảng trắng trong tên của chúng : for file in $(find .); do echo "$file"; done. Tên với không gian được phân chia, mà chúng tôi không muốn.
Jean-Philippe Pellet

Câu trả lời:


1140

Sử dụng công -prunetắc. Ví dụ: nếu bạn muốn loại trừ miscthư mục, chỉ cần thêm -path ./misc -prune -olệnh vào lệnh find:

find . -path ./misc -prune -o -name '*.txt' -print

Đây là một ví dụ với nhiều thư mục:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print

Ở đây chúng tôi loại trừ dir1 , dir2dir3 , vì trong findbiểu thức, đó là một hành động tác động đến các tiêu chí -path dir1 -o -path dir2 -o -path dir3(nếu dir1 hoặc dir2 hoặc dir3 ), ANDed với type -d.

Hành động hơn nữa là -o print, chỉ cần in.


89
Hừm. Điều này cũng không hiệu quả với tôi vì nó sẽ bao gồm thư mục bị bỏ qua "./misc" trong đầu ra.
Theuni

84
@Theuni Nó có thể không hoạt động với bạn vì bạn đã không thêm một -print(hoặc bất kỳ hành động nào khác) rõ ràng sau đó -name. Trong trường hợp đó, cả hai "mặt" của -oin cuối cùng, trong khi nếu bạn sử dụng -print, chỉ có mặt đó in.
Daniel C. Sobral

4
Từ manpage: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Vì vậy, làm cách nào để xóa bằng find nếu tôi muốn loại trừ các thư mục cụ thể khỏi việc xóa?
Jāni Elmeris

15
Để xóa toàn bộ thư mục khỏi kết quả, hãy sử dụng : find . -not -path "./.git*". Sử dụng ./dir*thay vì ./dir/*loại bỏ thư mục cũng như nội dung từ đầu ra.
micahblu

64
Câu hỏi này và sự nhầm lẫn trong các câu trả lời là một biểu hiện về mức độ xấu của giao diện người dùng tìm thấy phù hợp với những gì mọi người cần.
Julian Overmann

1932

Nếu -prunekhông làm việc cho bạn, điều này sẽ:

find -name "*.js" -not -path "./directory/*"

Hãy cẩn thận: yêu cầu duyệt qua tất cả các thư mục không mong muốn.


86
Một trong những ý kiến ​​trong câu trả lời được chấp nhận chỉ ra vấn đề. -prunekhông loại trừ chính thư mục, nó loại trừ nội dung của nó, điều đó có nghĩa là bạn sẽ nhận được một dòng không mong muốn trong đầu ra với thư mục bị loại trừ.
GetFree

96
Câu trả lời chính xác. Tôi thêm vào điều này rằng bạn có thể loại trừ một thư mục ở cấp độ BẤT K by bằng cách thay đổi thư mục đầu tiên .thành *. vì vậy find -name "*.js" -not -path "*/omitme/*"sẽ bỏ qua các tệp từ một thư mục có tên "omitme" ở bất kỳ mức độ sâu nào.
DeeDee

83
Nó vẫn đi qua tất cả các thư mục không mong muốn, mặc dù. Tôi đang thêm câu trả lời của riêng tôi. :-)
Daniel C. Sobral

18
Tuy nhiên, lưu ý rằng tùy chọn cắt tỉa chỉ không hoạt động nếu bạn không sử dụng -printrõ ràng.
Daniel C. Sobral

39
Sẽ là tốt hơn để nói "Đây là một thay thế cho việc sử dụng -prune". Các câu trả lời gợi ý -prune rõ ràng không sai, chúng không phải là cách bạn sẽ làm.
Jimbo

458

Tôi thấy những lý do sau đây dễ giải thích hơn các giải pháp đề xuất khác:

find build -not \( -path build/external -prune \) -name \*.js
# you can also exclude multiple paths
find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

Lưu ý quan trọng: các đường dẫn bạn nhập sau -pathphải khớp chính xác với những gì findsẽ in mà không loại trừ. Nếu câu này gây nhầm lẫn, bạn chỉ cần đảm bảo sử dụng các đường dẫn đầy đủ thông qua toàn bộ lệnh như thế này : . Xem ghi chú [1] nếu bạn muốn hiểu rõ hơn.find /full/path/ -not \( -path /full/path/exclude/this -prune \) ...

Bên trong \(\)là một biểu thức sẽ khớp chính xác build/external (xem ghi chú quan trọng ở trên), và sẽ thành công, tránh vượt qua bất cứ điều gì bên dưới . Điều này sau đó được nhóm lại dưới dạng một biểu thức với dấu ngoặc đơn được thoát và tiền tố -notsẽ findbỏ qua bất kỳ thứ gì khớp với biểu thức đó.

Người ta có thể hỏi nếu thêm -notsẽ không làm cho tất cả các tệp khác bị ẩn bằng cách -prunexuất hiện lại và câu trả lời là không. Cách thức -prunehoạt động là bất cứ điều gì, một khi nó đạt được, các tệp bên dưới thư mục đó sẽ bị bỏ qua vĩnh viễn.

Điều này xuất phát từ một trường hợp sử dụng thực tế, trong đó tôi cần gọi yui-nén trên một số tệp được tạo bởi wintersmith, nhưng bỏ qua các tệp khác cần được gửi như hiện trạng.


Lưu ý [1] : Nếu bạn muốn loại trừ /tmp/foo/barvà bạn chạy find như thế này " find /tmp \(..." thì bạn phải chỉ định -path /tmp/foo/bar. Nếu mặt khác bạn chạy tìm như thế này cd /tmp; find . \(...thì bạn phải chỉ định -path ./foo/bar.


37
Câu trả lời nổi bật, cảm ơn bạn. Điều này hoạt động và có thể mở rộng (có thể đọc được) cho nhiều loại trừ. Bạn là một quý ông và một học giả thưa ông. Cảm ơn bạn vì ví dụ cho nhiều loại trừ
Freedom_Ben

7
Điều này không hoạt động nếu tôi muốn sử dụng công tắc -delete:find . -not \( -path ./CVS -prune \) -type f -mtime +100 -delete find: The -delete action atomatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
Jāni Elmeris 14/12/13

17
@Janis Bạn có thể sử dụng -exec rm -rf {} \;thay vì -delete.
Daniel C. Sobral

11
Bằng cách kiểm tra đầu ra của find, điều này thực sự rõ ràng, nhưng nó làm tôi vấp ngã. Nếu bạn đang tìm kiếm trong thư mục hiện tại (bằng cách chỉ định .là đường dẫn tìm kiếm hoặc hoàn toàn không chỉ định một đường dẫn), rất có thể bạn muốn mẫu của bạn sau khi -pathbắt đầu ./, ví dụ : find -not \( -path ./.git -prune \) -type f.
Zantier

7
Một biến thể chính xác hơn (và tương thích POSIX) của phương pháp này: find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)theo sau là bất kỳ điều kiện nào phù hợp với những gì bạn đang tìm kiếm.
Walf

218

Rõ ràng có một số nhầm lẫn ở đây là cú pháp ưa thích để bỏ qua một thư mục nên là gì.

Ý kiến ​​của GNU

To ignore a directory and the files under it, use -prune

Từ trang tìm GNU

Lý luận

-prunedừng findtừ giảm dần vào một thư mục. Chỉ cần xác định -not -pathsẽ vẫn rơi vào thư mục bị bỏ qua , nhưng -not -pathsẽ sai bất cứ khi nàofind kiểm tra từng tệp.

Các vấn đề với -prune

-prune thực hiện những gì nó dự định, nhưng vẫn là một số điều bạn phải quan tâm khi sử dụng nó.

  1. find in thư mục cắt tỉa.

    • ĐÚNG Đó là hành vi dự định, nó chỉ không rơi vào nó. Để tránh in hoàn toàn thư mục, hãy sử dụng cú pháp bỏ qua nó một cách hợp lý.
  2. -prunechỉ hoạt động với -printvà không có hành động khác.

    • KHÔNG ĐÚNG . -prunelàm việc với bất kỳ hành động nào ngoại trừ -delete. Tại sao nó không hoạt động với xóa? Để -deletelàm việc, tìm nhu cầu duyệt qua thư mục theo thứ tự DFS, vì -deletetrước tiên sẽ xóa các lá, sau đó là cha mẹ của các lá, v.v ... Nhưng để chỉ định -prunecó ý nghĩa, findcần phải nhấn một thư mục và ngừng hạ xuống nó, mà rõ ràng không có ý nghĩa với -depthhoặc -deletetrên.

Hiệu suất

Tôi thiết lập một bài kiểm tra đơn giản về ba câu trả lời nâng cao hàng đầu cho câu hỏi này (được thay thế -printbằng -exec bash -c 'echo $0' {} \;để hiển thị một ví dụ hành động khác). Kết quả dưới đây

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

Phần kết luận

Cả cú pháp của f10bitcú pháp của Daniel C. Sobral đều mất trung bình 10-25ms. Cú pháp của GetFree , không sử dụng -prune, mất 865ms. Vì vậy, vâng, đây là một ví dụ khá cực đoan, nhưng nếu bạn quan tâm đến thời gian chạy và đang làm bất cứ điều gì từ xa, bạn nên sử dụng -prune.

Lưu ý Cú pháp của Daniel C. Sobral thực hiện tốt hơn hai -prunecú pháp; nhưng, tôi hoàn toàn nghi ngờ đây là kết quả của một số bộ nhớ đệm khi chuyển đổi thứ tự mà cả hai đã chạy dẫn đến kết quả ngược lại, trong khi phiên bản không cắt tỉa luôn chậm nhất.

Kiểm tra tập lệnh

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup

18
Cảm ơn bạn đã phân tích rất tốt. Về "Tôi hoàn toàn nghi ngờ đây là kết quả của một số bộ đệm", bạn có thể chạy lệnh này: sudo sh -c "free && sync && echo 3> / Proc / sys / vm / drop_caches && free" để xóa bộ đệm (xem unix. stackexchange.com/questions/87908/ Mạnh ).
ndemou

Sau một vài thử nghiệm trên hai cái đó với -prunetôi có thể thấy hiếm khi có sự khác biệt. Hãy nhớ rằng lệnh nào bắt đầu trước sẽ được hưởng lợi từ hiệu suất cpu, việc cpu khởi động sau đó làm giảm hiệu suất làm chậm một chút (Tôi đã xóa bộ đệm trước mỗi lệnh như đề xuất @ndemou)
Huy.PhamNhu

Hãy thử chuyển đổi số trong name1() name2() name3()tập lệnh kiểm tra @BroSlow ở trên để thay đổi thứ tự thực hiện để có được hình dung về những gì tôi đã nói. Trong cuộc sống thực, không thể nhận thấy giữa hai người đó.
Huy.PhamNhu

Vỗ tay. Cảm ơn bạn cho câu trả lời chất lượng này.
Stphane

Bạn không nên -o có nghĩa là hoặc. Vì vậy, bạn đang cắt tỉa trong bước đầu tiên và sau đó quên tất cả về nó trong bước tiếp theo.
mmm

97

Đây là người duy nhất làm việc cho tôi.

find / -name MyFile ! -path '*/Directory/*'

Tìm kiếm "MyFile" không bao gồm "Thư mục". Hãy nhấn mạnh vào các ngôi sao *.


13
Phương pháp này hoạt động trên macOS trong khi câu trả lời được chấp nhận thì không. Tôi biết câu hỏi ban đầu là dành cho Linux.
Xavier Rubio Jansana

5
Lưu ý rằng bạn có thể thêm nhiều ! -path '*/Directory/*'vào lệnh của mình liên tiếp để bỏ qua nhiều thư mục
Aclwitt

Hoạt động trên MacOS nhưng không có trên linux ... đã được xác nhận
Marcello de Sales

Trong một docker containerhoạt động duy nhất vớish -c "find..."
Marcello de Sales

@Marcello de Sales Tất nhiên nó hoạt động trên Linux.
DimiDak

59

Một tùy chọn sẽ là loại trừ tất cả các kết quả có chứa tên thư mục với grep. Ví dụ:

find . -name '*.js' | grep -v excludeddir

44
Điều này sẽ làm cho tìm kiếm của bạn rất chậm
Dorian

6
Cái này làm việc cho tôi, những cái khác (sử dụng -prune) - không.
Andron

7
Chậm trong kết quả lớn, nhưng hữu ích trong các bộ nhỏ hơn. Nhưng làm thế nào để loại trừ nhiều thư mục bằng grep? Tất nhiên theo cách này: find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3nhưng có thể có một cách nào đó.
Timo Kähkönen

6
Nếu bạn muốn thực hiện nhiều greps thì tốt hơn hết bạn nên viết nó dưới dạng biểu thức thông thường : egrep -v '(dir1|dir2|dir3)'. Tuy nhiên, trong nghiên cứu trường hợp cụ thể này, sẽ tốt hơn nếu loại trừ các thư mục trong findchính nó.
Laurence

1
có, và bạn không cần dấu ngoặc đơn và sẽ tốt hơn khi sử dụng ^ để đảm bảo nó khớp với directoryname khi bắt đầu chuỗi, ví dụ: find. -name '* .js' | egrep -v "^ \ ./ Elimeddir1 | ^ \ ./ Elimeddir2"
Sofija

41

Tôi thích -notký hiệu này ... nó dễ đọc hơn:

find . -name '*.js' -and -not -path directory

5
Xin lỗi, nó không hoạt động. Trang hướng dẫn cho findbiết: "Để bỏ qua một thư mục và các tệp trong đó, hãy sử dụng -prune".
Christian Davén

8
Cái này sai. Nó không ngăn tìm thấy vào thư mục và duyệt qua tất cả các tệp bên trong.
GetFree

find . -iname '*' -and -not -path './somePath'không ngăn nó vào thư mục nói.
Lemmings19

Điều này đã giúp tôi với con đường .git find . -iname '*' -not -path './.git/*'
Mark Shust tại M.academy

7
@rane: Cụ thể hơn find . -not -path "*/.git*"sẽ là những gì bạn muốn.
Ben

20

Sử dụng tùy chọn -prune. Vì vậy, một cái gì đó như:

find . -type d -name proc -prune -o -name '*.js'

'-Type d -name Proc -prune' chỉ tìm các thư mục có tên là Proc để loại trừ.
'-O' là toán tử 'HOẶC'.


1
Đây là giải pháp "tìm" thuần túy duy nhất phù hợp với tôi. Các thư mục tôi muốn loại trừ KHÔNG ngay bên dưới thư mục làm việc hiện tại.
Lambart

5
Tuy nhiên, thêm -printvào cuối có thể cải thiện kết quả. find . -type d -name .hg -prune -o -name databỏ qua nội dung của các thư mục (nhiều) .hg, nhưng liệt kê các .hgthư mục đó. Với -print, nó chỉ liệt kê các thư mục "dữ liệu" mà tôi đang tìm kiếm.
Lambart

19

-prunechắc chắn hoạt động và là câu trả lời tốt nhất bởi vì nó ngăn chặn việc đi xuống vào thư mục mà bạn muốn loại trừ. -not -pathvẫn tìm kiếm thư mục bị loại trừ, nó chỉ không in kết quả, đây có thể là một vấn đề nếu thư mục bị loại trừ được gắn âm lượng mạng hoặc bạn không có quyền.

Phần khó khăn findlà rất đặc biệt về thứ tự của các đối số, vì vậy nếu bạn không hiểu đúng, lệnh của bạn có thể không hoạt động. Thứ tự của các đối số thường là như vậy:

find {path} {options} {action}

{path}: Đặt tất cả các đối số liên quan đến đường dẫn trước, như . -path './dir1' -prune -o

{options}: Tôi có nhiều thành công nhất khi đặt -name, -iname, etclàm lựa chọn cuối cùng trong nhóm này. Ví dụ-type f -iname '*.js'

{action}: Bạn sẽ muốn thêm -printkhi sử dụng-prune

Đây là một ví dụ hoạt động:

# setup test
mkdir dir1 dir2 dir3
touch dir1/file.txt; touch dir1/file.js
touch dir2/file.txt; touch dir2/file.js
touch dir3/file.txt; touch dir3/file.js

# search for *.js, exclude dir1
find . -path './dir1' -prune -o -type f -iname '*.js' -print

# search for *.js, exclude dir1 and dir2
find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print

16

Đây là định dạng tôi đã sử dụng để loại trừ một số đường dẫn:

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"

Tôi đã sử dụng điều này để tìm tất cả các tệp không nằm trong đường dẫn ". *":

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"

Tôi đã thử điều này và nó vẫn đi vào thư mục, vì vậy tốc độ chắc chắn không được cải thiện.
Br.Bill

10

Cách tiếp cận -path -prune cũng hoạt động với các ký tự đại diện trong đường dẫn. Dưới đây là một tuyên bố tìm sẽ tìm thấy các thư mục cho một máy chủ git phục vụ nhiều repositiories git rời khỏi các thư mục nội bộ git:

find . -type d \
   -not \( -path */objects -prune \) \
   -not \( -path */branches -prune \) \
   -not \( -path */refs -prune \) \
   -not \( -path */logs -prune \) \
   -not \( -path */.git -prune \) \
   -not \( -path */info -prune \) \
   -not \( -path */hooks -prune \)  

9

Để loại trừ nhiều thư mục:

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)

Để thêm thư mục, thêm -o -path "./dirname/*":

find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)

Nhưng có lẽ bạn nên sử dụng một biểu thức chính quy , nếu có nhiều thư mục để loại trừ.


9

Có rất nhiều câu trả lời hay, tôi chỉ mất một chút thời gian để hiểu từng yếu tố của lệnh là gì và logic đằng sau nó.

find . -path ./misc -prune -o -name '*.txt' -print

find sẽ bắt đầu tìm tập tin và thư mục trong thư mục hiện tại, do đó find ..

Các -otùy chọn là viết tắt của một logic OR và tách hai phần của lệnh:

[ -path ./misc -prune ] OR [ -name '*.txt' -print ]

Bất kỳ thư mục hoặc tệp nào không phải là thư mục ./misc sẽ không vượt qua bài kiểm tra đầu tiên -path ./misc. Nhưng họ sẽ được kiểm tra chống lại biểu thức thứ hai. Nếu tên của họ tương ứng với mẫu *.txthọ được in, vì -printtùy chọn.

Khi find đạt đến thư mục ./misc, thư mục này chỉ thỏa mãn biểu thức đầu tiên. Vì vậy, -prunetùy chọn sẽ được áp dụng cho nó. Nó báo lệnh find để không khám phá thư mục đó. Vì vậy, bất kỳ tệp hoặc thư mục nào trong ./misc thậm chí sẽ không được khám phá bằng find, sẽ không được kiểm tra đối với phần thứ hai của biểu thức và sẽ không được in.


Mọi người đều có giải pháp nhưng bạn giải thích nó là tốt nhất. Tôi đã kiên quyết để -name được sử dụng trước chứ không phải -path. Giải thích của bạn là đủ để đi đến những gì tôi muốn. tìm thấy . -name "* .txt" -print -o -path ./misc -prune
Vendetta V

7

Đối với một giải pháp hoạt động (đã thử nghiệm trên Ubuntu 12.04 (Tê tê chính xác)) ...

find ! -path "dir1" -iname "*.mp3"

sẽ tìm kiếm các tệp MP3 trong thư mục hiện tại và các thư mục con ngoại trừ trong thư mục con dir1.

Sử dụng:

find ! -path "dir1" ! -path "dir2" -iname "*.mp3"

... để loại trừ dir1 VÀ dir2


Không làm việc cho tôi. Không có bất kỳ câu trả lời trên. Mũ đỏ.
Tharpa

6

Một mẹo hay để tránh in các thư mục bị cắt xén là sử dụng -print(cũng hoạt động -exec) sau phần bên phải của phần -orsau -prune. Ví dụ, ...

find . -path "*/.*" -prune -or -iname "*.j2"

sẽ in đường dẫn của tất cả các tệp bên dưới thư mục hiện tại có phần mở rộng `.j2", bỏ qua tất cả các thư mục bị ẩn. Neat. sau đây không, ...

find . -path "*/.*" -prune -or -iname "*.j2" -print

bởi vì về mặt logic, có một ẩn -andsau -inametoán tử và trước dấu. Điều này liên kết nó với phần bên phải của -ormệnh đề do thứ tự boolean của hoạt động và tính kết hợp. Nhưng các tài liệu nói rằng có một ẩn -printnếu nó (hoặc bất kỳ anh em họ của nó ... -print0, v.v.) không được chỉ định. Vậy tại sao không phải là phần bên trái của -orin ấn? Rõ ràng (và tôi đã không hiểu điều này từ lần đầu tiên đọc trang nam), điều đó đúng nếu không có người vận hành mô tả rõ ràng sẽ không làm gì cả, vì vậy tôi đoán nó có ý nghĩa như vậy. Như đã đề cập ở trên, tất cả đều hoạt động tốt, do đó, phần sau cung cấp danh sách đầy đủ cho mỗi tệp với phần mở rộng mong muốn, nhưng không liệt kê cấp độ đầu tiên của mỗi thư mục ẩn, ...-print - -execBẤT K AN LÚC NÀO, trong trường hợp đó, - dấu vết được rắc một cách hợp lý để mọi thứ được in. Nếu thậm chí MỘTprinthoạt động kiểu được thể hiện trong bất kỳ mệnh đề nào, tất cả những điều logic ẩn đó sẽ biến mất và bạn chỉ nhận được những gì bạn chỉ định. Bây giờ, thẳng thắn, tôi có thể thích nó theo cách khác, nhưng sau đó một ngườifind-execls -la

find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +

Đối với tôi (và những người khác trong chủ đề này), findcú pháp trở nên khá baroque khá nhanh, vì vậy tôi luôn ném vào parens để làm cho SURE tôi biết những gì liên kết với cái gì, vì vậy tôi thường tạo một macro cho khả năng loại và tạo thành tất cả các câu như vậy. ..

find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)

Thật khó để đi sai bằng cách thiết lập thế giới thành hai phần theo cách này. Tôi hy vọng điều này có ích, mặc dù dường như không ai có thể đọc được câu trả lời thứ 30 + và bỏ phiếu, nhưng người ta có thể hy vọng. :-)


5

Bạn có thể sử dụng tùy chọn cắt tỉa để đạt được điều này. Như trong ví dụ:

find ./ -path ./beta/* -prune -o -iname example.com -print

Hoặc tùy chọn grep -v nghịch đảo grep -v:

find -iname example.com | grep -v beta

Bạn có thể tìm thấy các hướng dẫn chi tiết và ví dụ trong Linux find lệnh loại trừ các thư mục khỏi tìm kiếm .


Giải pháp grep là giải pháp duy nhất loại trừ tất cả các thư mục có cùng tên. Khi cố gắng loại trừ "node_modules" khá hữu ích.
bmacnaughton

3
@bmacnaughton - không đúng! Tôi đến đây đặc biệt tìm cách loại trừ "node_modules" và sau khi đọc nhiều câu trả lời hay, tôi đã giải quyết find . -type f -print -o -path "*/node_modules" -prune... bằng cách sử dụng ký tự đại diện này bỏ qua "node_modules" ở mọi cấp độ; việc sử dụng -printthay thế đầu tiên -type f -printchỉ tạo ra phần in đó, vì vậy các thư mục "node_modules" không được liệt kê. (nó cũng có thể được đảo ngược find . -path "*/node_modules" -prune -o -type f -print:)
Stephen P

* / đang làm gì ở đó Tệp chính xác bạn muốn loại trừ là gì. Có phải bạn đang sử dụng nó làm ký tự đại diện không?
Siju V

1
@StephenP, cảm ơn bạn đã chỉ ra điều này; Tôi đã học được sự khác biệt giữa việc sử dụng ./node_modules*/node_modulestừ nó. Đối với trường hợp của tôi, nơi node_moduleschỉ tồn tại trong thư mục tôi bắt đầu tìm kiếm trong (và trong node_modulesthư mục đó ), tôi có thể sử dụng find . -type f -print -o -path "./node_modules" -prune vì sẽ không có node_modulesthư mục trong bất kỳ thư mục nào khác.
bmacnaughton

1
@SijuV - trong thư mục mà tôi đang tìm kiếm có một node_modulesthư mục con, nhưng cũng có các thư mục con có nút_modules riêng ... chỉ sử dụng ./node_moduleskhớp với thư mục con node_modulestrong thư mục hiện tại .và cắt xén nó; sử dụng */node_moduleskhớp và cắt thư mục ở bất kỳ độ sâu nào, bởi vì *như một quả cầu khớp với bất kỳ tiền tố đường dẫn hàng đầu nào, chẳng hạn như ./test5/main/node_modules, không chỉ ./tiền tố. Đây *là một ký tự đại diện, nhưng như một quả địa cầu không phải là một biểu thức chính quy.
Stephen P

5
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune

Không thể có được cái này để làm việc. find ~/Projects -name '*.js' -\! -name 'node_modules' -prunevẫn đang bật các tập tin node_modulestrong đường dẫn của họ
mpen

1
@mpen, Từ stackoverflow.com/questions/4210042/ , tôi đã học được rằng cú pháp bạn muốn là find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Tên của đường dẫn đó phải khớp chính xác với những gì tìm thấy sẽ in nếu nó sẽ in thư mục.
PatS

4
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'

dường như làm việc giống như

find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)

và dễ nhớ IMO hơn.


4

TLDR: hiểu các thư mục gốc của bạn và điều chỉnh tìm kiếm của bạn từ đó, bằng cách sử dụng -path <excluded_path> -prune -otùy chọn. Không bao gồm một dấu /ở cuối con đường bị loại trừ.

Thí dụ:

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print


Để sử dụng hiệu quả, findtôi tin rằng bắt buộc phải hiểu rõ về cấu trúc thư mục hệ thống tệp của bạn. Trên máy tính ở nhà của tôi, tôi có ổ cứng nhiều TB, với khoảng một nửa nội dung được sao lưu bằng cách sử dụng rsnapshot(nghĩa là rsync). Mặc dù sao lưu vào ổ đĩa độc lập (trùng lặp) vật lý, nó được gắn trong thư mục root ( /) của hệ thống của tôi /mnt/Backups/rsnapshot_backups/::

/mnt/Backups/
└── rsnapshot_backups/
    ├── hourly.0/
    ├── hourly.1/
    ├── ...
    ├── daily.0/
    ├── daily.1/
    ├── ...
    ├── weekly.0/
    ├── weekly.1/
    ├── ...
    ├── monthly.0/
    ├── monthly.1/
    └── ...

Thư mục /mnt/Backups/rsnapshot_backups/hiện chiếm ~ 2,9 TB, với ~ 60M tệp và thư mục; chỉ đơn giản là đi qua những nội dung đó cần có thời gian:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find /mnt/Backups/rsnapshot_backups | wc -l
60314138    ## 60.3M files, folders
34:07.30    ## 34 min

time du /mnt/Backups/rsnapshot_backups -d 0
3112240160  /mnt/Backups/rsnapshot_backups    ## 3.1 TB
33:51.88    ## 34 min

time rsnapshot du    ## << more accurate re: rsnapshot footprint
2.9T    /mnt/Backups/rsnapshot_backups/hourly.0/
4.1G    /mnt/Backups/rsnapshot_backups/hourly.1/
...
4.7G    /mnt/Backups/rsnapshot_backups/weekly.3/
2.9T    total    ## 2.9 TB, per sudo rsnapshot du (more accurate)
2:34:54          ## 2 hr 35 min

Vì vậy, bất cứ khi nào tôi cần tìm kiếm một tệp trên /phân vùng (root) của mình, tôi cần phải xử lý (tránh nếu có thể) đi qua phân vùng sao lưu của tôi.


VÍ DỤ

Trong số các cách tiếp cận được đề xuất khác nhau trong chủ đề này ( Cách loại trừ một thư mục trong lệnh find. ), Tôi thấy rằng các tìm kiếm sử dụng câu trả lời được chấp nhận là rất nhiều nhanh hơn - với sự cẩn thận.

Giải pháp 1

Giả sử tôi muốn tìm tệp hệ thống libname-server-2.a, nhưng tôi không muốn tìm kiếm thông qua các rsnapshotbản sao lưu của mình . Để nhanh chóng tìm thấy tệp hệ thống, hãy sử dụng đường dẫn loại trừ /mnt(nghĩa là sử dụng /mnt, không /mnt/, hoặc /mnt/Backups, hoặc ...):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a
real    0m8.644s              ## 8.6 sec  <<< NOTE!
user    0m1.669s
 sys    0m2.466s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 3 sec     ## ~3 sec  <<< NOTE!

... Tìm thấy tệp đó chỉ trong vài giây, trong khi quá trình này mất nhiều thời gian hơn (dường như được lặp lại qua tất cả các thư mục "bị loại trừ"):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt/ -prune -o -name "*libname-server-2.a*" -print
find: warning: -path /mnt/ will not match anything because it ends with /.
/usr/lib/libname-server-2.a
real    33m10.658s            ## 33 min 11 sec (~231-663x slower!)
user    1m43.142s
 sys    2m22.666s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt/ -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 1775 sec    ## 29.6 min

Giải pháp 2

Giải pháp khác được cung cấp trong chuỗi này ( SO # 4210042 ) cũng hoạt động kém:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -name "*libname-server-2.a*" -not -path "/mnt"
/usr/lib/libname-server-2.a
real    33m37.911s            ## 33 min 38 sec (~235x slower)
user    1m45.134s
 sys    2m31.846s

time find / -name "*libname-server-2.a*" -not -path "/mnt/*"
/usr/lib/libname-server-2.a
real    33m11.208s            ## 33 min 11 sec
user    1m22.185s
 sys    2m29.962s

TÓM TẮT | KẾT LUẬN

Sử dụng phương pháp minh họa trong " Giải pháp 1 "

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print

I E

... -path <excluded_path> -prune -o ...

lưu ý rằng bất cứ khi nào bạn thêm dấu vết /vào đường dẫn bị loại trừ, findlệnh sẽ truy xuất đệ quy (tất cả các /mnt/*thư mục ) - trong trường hợp của tôi, vì các /mnt/Backups/rsnapshot_backups/*thư mục con, ngoài ra còn bao gồm ~ 2,9 TB tệp để tìm kiếm! Bằng cách không nối thêm một dấu /, tìm kiếm sẽ hoàn thành gần như ngay lập tức (trong vài giây).

"Giải pháp 2" ( ... -not -path <exclude path> ...) tương tự xuất hiện để tìm kiếm đệ quy thông qua các thư mục bị loại trừ - không trả về kết quả khớp bị loại trừ, nhưng không cần thiết phải tiêu tốn thời gian tìm kiếm đó.


Tìm kiếm trong những rsnapshot bản sao lưu đó:

Để tìm một tệp trong một rsnapshotbản sao lưu hàng giờ / hàng ngày / hàng tuần / hàng tháng của tôi ):

$ START="$(date +"%s")" && find 2>/dev/null /mnt/Backups/rsnapshot_backups/daily.0 -name '*04t8ugijrlkj.jpg'; END="$(date +"%s")"; TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg
find command took 312 sec   ## 5.2 minutes: despite apparent rsnapshot size
                            ## (~4 GB), it is in fact searching through ~2.9 TB)

Không bao gồm một thư mục lồng nhau:

Ở đây, tôi muốn loại trừ một thư mục lồng nhau, ví dụ: /mnt/Vancouver/projects/ie/claws/data/*khi tìm kiếm từ /mnt/Vancouver/projects/:

$ time find . -iname '*test_file*'
./ie/claws/data/test_file
./ie/claws/test_file
0:01.97

$ time find . -path '*/data' -prune -o -iname '*test_file*' -print
./ie/claws/test_file
0:00.07

Ngoài ra: Thêm -printvào cuối lệnh sẽ ngăn chặn bản in của thư mục bị loại trừ:

$ find / -path /mnt -prune -o -name "*libname-server-2.a*"
/mnt
/usr/lib/libname-server-2.a

$ find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a

Nó không phải là kích thước của các tệp làm chậm find, đó là số lượng mục nhập thư mục mà nó phải kiểm tra. Vì vậy, sẽ tệ hơn nhiều nếu bạn có nhiều, nhiều tệp nhỏ (đặc biệt là nếu tất cả chúng đều được liên kết nhiều lần!) So với việc bạn chỉ có một số tệp nhiều gigabyte.
Toby Speight

@TobySpeight: điểm tốt. Tôi đã đề cập đến kích thước không gian tìm kiếm để chỉ ra tỷ lệ, cũng chứa nhiều tệp. Tìm kiếm nhanh gốc (/) với sudo ls -R / | wc -lchỉ ra ~ 76,5M tệp (hầu hết được sao lưu ngoại trừ tệp hệ thống "không cấu hình"); /mnt/Vancouver/với ls -R | wc -lchỉ số ~ 2,35M; /home/victoria/chứa các tệp 0,668M.
Victoria Stuart

4

Bạn cũng có thể sử dụng các biểu thức thông thường để bao gồm / loại trừ một số tệp / thư mục tìm kiếm của bạn bằng cách sử dụng một cái gì đó như thế này:

find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*" 

Điều này sẽ chỉ cung cấp cho bạn tất cả các tệp js, vue, css, vv nhưng không bao gồm tất cả các tệp trong node_modulesvendorthư mục.


3

Tôi đã sử dụng findđể cung cấp một danh sách các tập tin xgettextvà muốn bỏ qua một thư mục cụ thể và nội dung của nó. Tôi đã thử nhiều hoán vị -pathkết hợp với -prunenhưng không thể loại trừ hoàn toàn thư mục mà tôi muốn đi.

Mặc dù tôi có thể bỏ qua nội dung của thư mục mà tôi muốn bỏ qua, findsau đó trả lại chính thư mục đó là một trong những kết quả, điều này gây raxgettext đến sự cố do kết quả (không chấp nhận thư mục; chỉ các tệp).

Giải pháp của tôi chỉ đơn giản là sử dụng grep -vđể bỏ qua thư mục mà tôi không muốn trong kết quả:

find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext

Có hay không có một đối số cho findđiều đó sẽ hoạt động 100%, tôi không thể nói chắc chắn. Sử dụng greplà một giải pháp nhanh chóng và dễ dàng sau một số đau đầu.


3

Không có câu trả lời nào trước đây là tốt trên Ubuntu. Thử cái này:

find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"

Tôi đã tìm thấy điều này ở đây


Tôi không thấy bất kỳ lý do nào khiến bất kỳ câu trả lời nào có hơn 100 điểm không hoạt động trên Ubuntu.
Axel Beckert

Mmm chúng ta hãy xem? có lẽ bởi vì tôi đã thử tất cả chúng?
Sixro

tìm thấy ở khắp mọi nơi cùng một triển khai trên tất cả các bản phân phối Linux - một trong số các Dự án GNU. Sự khác biệt duy nhất có thể là các phiên bản. Nhưng những thay đổi trong thập kỷ qua không phải là xâm lấn, ngoại trừ có thể cho phép khớp.
Axel Beckert

3

Điều này phù hợp với tôi trên máy Mac:

find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune

Nó sẽ loại trừ vendorapp/cachedir cho tên tìm kiếm có hậu tố php.


Tốt hơn nên đặt dấu ngoặc đơn xung quanh '* .php' hoặc bạn sẽ không tìm thấy những gì bạn đang tìm kiếm.
Br.Bill

3

Dành cho những bạn dùng phiên bản UNIX cũ hơn, những người không thể sử dụng -path hoặc -not

Đã thử nghiệm trên SunOS 5.10 bash 3.2 và SunOS 5.11 bash 4.4

find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f

Có thể vượt qua nhiều hơn thư mục được chỉ định.
MUY Bỉ

2

how-to-use-prune-option-of-find-in-sh là một câu trả lời tuyệt vời của Laurence Gonsalves về cách thức -prunehoạt động.

Và đây là giải pháp chung:

find /path/to/search                    \
  -type d                               \
    \( -path /path/to/search/exclude_me \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print

Để tránh gõ /path/to/seach/nhiều lần, hãy bọc findtrong một pushd .. popdcặp.

pushd /path/to/search;                  \
find .                                  \
  -type d                               \
    \( -path ./exclude_me               \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print;         \
 popd

1
Từ stackoverflow.com/questions/4210042/ , tôi đã học được rằng cú pháp được sử dụng cho tên -pathphải tìm sẽ in nếu nó là để in thư mục, ví dụ find . -path ./.git -prune -o -print, hoặc find $HOME/foo -path $HOME/foo/.git -prune -o -print một số câu trả lời chỉ nói -path somedirkhông may là không đủ chính xác để có ích
PatS

2

Đối với những gì tôi cần, nó hoạt động như thế này, tìm landscape.jpgtrong tất cả các máy chủ bắt đầu từ root và loại trừ tìm kiếm trong /varthư mục:

find / -maxdepth 1 -type d | grep -v /var | xargs -I '{}' find '{}' -name landscape.jpg

find / -maxdepth 1 -type dliệt kê tất cả d irectories trong/

grep -v /var loại trừ `/ var 'khỏi danh sách

xargs -I '{}' find '{}' -name landscape.jpgthực hiện bất kỳ lệnh nào, như findvới mỗi thư mục / kết quả từ danh sách


Đợi một chút, /vẫn chưa được loại trừ. Bạn có thể cần sed 1d.
Simba

2

Các lệnh sau hoạt động:

find . -path ./.git -prune -o -print

Nếu bạn gặp vấn đề với find, hãy sử dụng -D treetùy chọn để xem thông tin phân tích biểu thức.

find -D tree . -path ./.git -prune -o -print

Hoặc -D all, để xem tất cả các thông tin thực hiện.

find -D all . -path ./.git -prune -o -print

1

Tôi tìm thấy tên hàm trong các tệp nguồn C loại trừ * .o và loại trừ * .swp và loại trừ (không phải tệp thông thường) và loại trừ đầu ra dir với lệnh này:

find .  \( ! -path "./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach

1

Sử dụng exechành động tốt hơn forvòng lặp:

find . -path "./dirtoexclude" -prune \
    -o -exec java -jar config/yuicompressor-2.4.2.jar --type js '{}' -o '{}' \;

Các exec ... '{}' ... '{}' \;sẽ được thực hiện một lần cho mỗi tập tin phù hợp, thay thế niềng răng '{}'với tên file hiện hành.

Lưu ý rằng các dấu ngoặc nhọn được đặt trong dấu ngoặc đơn để bảo vệ chúng khỏi việc diễn giải dưới dạng dấu chấm câu shell * .


Ghi chú

* Từ phần EXAMPLES của find (GNU findutils) 4.4.2trang man


1
Câu hỏi rất cũ, nhưng vẫn còn chỗ để cải thiện. Tôi đã tìm thấy nó một cách tình cờ khi cố gắng giải quyết một vấn đề tương tự, và không có câu trả lời nào thỏa đáng.
Alberto

Tôi sử dụng exechành động thường xuyên và thấy nó rất hữu ích. Tôi thường thêm dấu ngoặc kép {}trong trường hợp có khoảng trắng trong đường dẫn tệp đưa ra "{}".
Ludovic Kuty

@lkuty Tôi chuẩn bị chỉnh sửa bài đăng của mình để phản ánh nhận xét của bạn, nhưng sau khi kiểm tra nhanh (không trích dẫn, {}không hoạt động đối với các tệp có khoảng trắng trong tên của họ) và xem xét các trang man, có vẻ như chỉ cần trích dẫn chúng bị hiểu sai là dấu chấm câu shell. Trong trường hợp này, bạn sẽ sử dụng trích dẫn duy nhất:'{}'
Alberto

Tôi nghĩ rằng tôi đã sử dụng nó để làm cphay mvhay rm. Tôi sẽ kiểm tra xem
Ludovic Kuty

1

Tôi đã thử lệnh ở trên, nhưng không ai trong số những người sử dụng "-prune" hoạt động với tôi. Cuối cùng, tôi đã thử điều này với lệnh dưới đây:

find . \( -name "*" \) -prune -a ! -name "directory"
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.