Cara mengecualikan direktori di find. perintah

1380

Saya mencoba menjalankan findperintah untuk semua file JavaScript, tetapi bagaimana cara mengecualikan direktori tertentu?

Ini findkode yang kami gunakan.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
helion3
sumber
10
Direktori apa yang perlu Anda kecualikan?
The Archetypal Paul
11
Lebih baik digunakan find ... | while read -r file .... Juga, lebih baik menerima dan meningkatkan jawaban.
Dijeda sampai pemberitahuan lebih lanjut.
sementara membaca lambat, karena dalam lebih cepat
mpapis
18
@mpapis saat membaca dengan benar menangani baris penuh dengan spasi.
Jean-Philippe Pellet
1
Hanya menjalankan ini dalam folder dengan file dengan spasi di nama mereka: for file in $(find .); do echo "$file"; done. Nama dengan spasi dibagi, yang tidak kami inginkan.
Jean-Philippe Pellet

Jawaban:

1140

Gunakan -prunesakelar. Misalnya, jika Anda ingin mengecualikan miscdirektori, tambahkan saja -path ./misc -prune -oke perintah find Anda:

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

Berikut ini adalah contoh dengan banyak direktori:

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

Di sini kita mengecualikan dir1 , dir2 dan dir3 , karena dalam findekspresi itu adalah tindakan yang bertindak berdasarkan kriteria -path dir1 -o -path dir2 -o -path dir3(jika dir1 atau dir2 atau dir3 ), ANDed with type -d.

Tindakan selanjutnya adalah -o print, cukup cetak.

f10bit
sumber
89
Hmm. Ini juga tidak berfungsi bagi saya karena akan memasukkan direktori yang diabaikan "./misc" di output.
Theuni
84
@Theuni Mungkin tidak berhasil untuk Anda karena Anda tidak menambahkan -print(atau tindakan lainnya) secara eksplisit setelahnya -name. Dalam hal ini, kedua "sisi" dari -opencetakan berakhir, sedangkan jika Anda gunakan -print, hanya sisi yang mencetak.
Daniel C. Sobral
4
Dari manpage: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Jadi, bagaimana cara menghapus menghapus jika saya ingin mengecualikan direktori tertentu dari penghapusan?
Jānis Elmeris
15
Untuk menghapus seluruh direktori itu sendiri dari penggunaan hasil: find . -not -path "./.git*". Menggunakan ./dir*alih-alih ./dir/*menghapus direktori serta konten dari output.
micahblu
64
Pertanyaan ini dan kebingungan dalam jawaban adalah manifes tentang betapa buruknya antarmuka pengguna yang cocok dengan apa yang orang butuhkan.
Johannes Overmann
1932

Jika -prunetidak bekerja untuk Anda, ini akan:

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

Peringatan: mengharuskan melintasi semua direktori yang tidak diinginkan.

Mendapatkan gratis
sumber
86
Salah satu komentar dalam jawaban yang diterima menunjukkan masalahnya. -prunetidak mengecualikan direktori itu sendiri, itu mengecualikan kontennya, yang berarti Anda akan mendapatkan baris yang tidak diinginkan dalam output dengan direktori yang dikecualikan.
GetFree
96
Jawaban yang bagus Saya akan menambahkan ini bahwa Anda dapat mengecualikan direktori di tingkat APAPUN dengan mengubah yang pertama .menjadi *. begitu juga find -name "*.js" -not -path "*/omitme/*"akan menghilangkan file dari direktori bernama "omitme" pada tingkat kedalaman apa pun.
DeeDee
83
Itu masih melintasi semua direktori yang tidak diinginkan. Saya menambahkan jawaban saya sendiri. :-)
Daniel C. Sobral
18
Perhatikan, bagaimanapun, bahwa opsi prune hanya tidak berfungsi jika Anda tidak menggunakan -printsecara eksplisit.
Daniel C. Sobral
39
Akan lebih baik untuk mengatakan "Ini adalah alternatif untuk menggunakan -prune". Jawaban-jawaban yang menyarankan -prune jelas tidak salah, mereka hanya tidak seperti yang Anda lakukan.
Jimbo
458

Saya menemukan alasan berikut lebih mudah daripada solusi yang diusulkan lainnya:

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

Catatan Penting: jalur yang Anda ketikkan -pathharus benar-benar cocok dengan apa yang findakan dicetak tanpa pengecualian. Jika kalimat ini membingungkan Anda hanya pastikan untuk menggunakan jalur penuh melalui keluar seluruh perintah seperti ini: . Lihat catatan [1] jika Anda ingin pemahaman yang lebih baik.find /full/path/ -not \( -path /full/path/exclude/this -prune \) ...

Di dalam \(dan \)merupakan ekspresi yang akan cocok persis build/external (lihat catatan penting di atas), dan akan, pada kesuksesan, menghindari melintasi apa pun di bawah ini . Ini kemudian dikelompokkan sebagai satu ekspresi dengan tanda kurung lolos, dan diawali dengan -notyang akan membuat findmelewatkan apa pun yang cocok dengan ekspresi itu.

Orang mungkin bertanya apakah menambahkan -nottidak akan membuat semua file lain disembunyikan oleh -prunemuncul kembali, dan jawabannya tidak. Cara -prunekerjanya adalah apa pun itu, setelah tercapai, file di bawah direktori tersebut secara permanen diabaikan.

Ini berasal dari use case yang sebenarnya, di mana saya perlu memanggil yui-compressor pada beberapa file yang dihasilkan oleh wintersmith, tetapi tinggalkan file lain yang perlu dikirim apa adanya.


Catatan [1] : Jika Anda ingin mengecualikan /tmp/foo/bardan menjalankan, temukan seperti ini " find /tmp \(..." maka Anda harus menentukan -path /tmp/foo/bar. Jika di sisi lain Anda menjalankan find seperti ini cd /tmp; find . \(...maka Anda harus menentukan -path ./foo/bar.

Daniel C. Sobral
sumber
37
Jawaban yang luar biasa, terima kasih. Ini berfungsi dan scalable (dapat dibaca) untuk beberapa pengecualian. Anda adalah tuan-tuan dan cendekiawan Pak. Terima kasih atas contoh untuk beberapa pengecualian
Freedom_Ben
7
Ini tidak berfungsi jika saya ingin menggunakan -delete switch: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ānis Elmeris
17
@ Janis Anda bisa menggunakan -exec rm -rf {} \;bukan -delete.
Daniel C. Sobral
11
Dengan memeriksa output dari find, ini jelas benar-benar, tapi itu membuatku tersandung. Jika Anda sedang mencari dalam direktori saat ini (dengan menetapkan .sebagai jalur pencarian, atau tidak menetapkan satu sama sekali), kemungkinan besar Anda ingin pola Anda setelah -pathuntuk memulai dengan ./, misalnya: find -not \( -path ./.git -prune \) -type f.
Zantier
7
Variasi yang lebih tepat (dan POSIX kompatibel) dari metode ini: find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)diikuti oleh kondisi apa pun yang harus cocok dengan yang Anda cari.
Walf
218

Jelas ada beberapa kebingungan di sini seperti apa sintaks yang disukai untuk melewatkan direktori.

Opini GNU

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

Dari halaman manual menemukan GNU

Pemikiran

-pruneberhenti findturun ke direktori. Hanya menentukan -not -pathmasih akan turun ke direktori yang dilewati , tetapi -not -pathakan menjadi salah setiap kali findmenguji setiap file.

Masalah dengan -prune

-prune melakukan apa yang dimaksudkan, tetapi masih ada beberapa hal yang harus Anda perhatikan ketika menggunakannya.

  1. find mencetak direktori yang sudah dipangkas.

    • BENAR Itu perilaku yang dimaksudkan, itu tidak turun ke dalamnya. Untuk menghindari pencetakan direktori sama sekali, gunakan sintaksis yang secara logis menghilangkannya.
  2. -prunehanya bekerja dengan -printdan tidak ada tindakan lain.

    • TIDAK BENAR . -prunebekerja dengan tindakan apa pun kecuali -delete. Mengapa itu tidak berhasil dengan delete? Agar -deletedapat bekerja, temukan kebutuhan untuk melintasi direktori dalam urutan DFS, karena -deletepertama-tama akan menghapus daun, lalu orang tua dari daun, dll ... Tetapi untuk menentukan -pruneagar masuk akal, findperlu menekan direktori dan berhenti turun, yang jelas tidak masuk akal dengan -depthatau -deletedi.

Performa

Saya membuat tes sederhana dari tiga jawaban teratas yang dipilih untuk pertanyaan ini (diganti -printdengan -exec bash -c 'echo $0' {} \;untuk menunjukkan contoh tindakan lain). Hasilnya di bawah

----------------------------------------------
# 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

Kesimpulan

Kedua sintaks f10bit ini dan sintaks Daniel C. Sobral ini mengambil 10-25ms untuk menjalankan rata-rata. Sintaks GetFree , yang tidak digunakan -prune, membutuhkan 865 ms. Jadi, ya ini adalah contoh yang agak ekstrem, tetapi jika Anda peduli dengan waktu yang diperlukan dan melakukan apa pun yang intensif, Anda harus menggunakannya -prune.

Catatan sintaksis Daniel C. Sobral melakukan yang lebih baik dari kedua -prunesintaksis; tapi, saya sangat curiga ini adalah hasil dari beberapa caching karena pergantian urutan di mana keduanya berlari menghasilkan hasil yang berlawanan, sedangkan versi non-prune selalu paling lambat.

Skrip Tes

#!/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
Tolong masukkan kembali Monica
sumber
18
Terima kasih atas analisis yang sangat bagus. Mengenai "Saya sangat curiga ini adalah hasil dari beberapa caching" Anda dapat menjalankan perintah ini: sudo sh -c "free && sync && echo 3> / proc / sys / vm / drop_caches && free" untuk menghapus cache (lihat unix. stackexchange.com/questions/87908/… ).
ndemou
Setelah beberapa tes pada mereka berdua dengan -prunesaya tahu jarang ada perbedaan. Perlu diingat bahwa perintah mana yang mulai lebih dulu akan mendapat manfaat dari kinerja cpu, penurunan kinerja cpu kemudian > menyebabkan minor melambat (saya melakukan pembersihan cache sebelum setiap perintah sebagai saran @ndemou)
Huy.PhamNhu
Coba alihkan nomor name1() name2() name3()di antara skrip uji @BroSlow di atas untuk mengubah perintah eksekusi untuk mendapatkan visual tentang apa yang saya katakan. Dalam kehidupan nyata, itu tidak terlalu mencolok di antara keduanya.
Huy.PhamNhu
Tepuk tangan. Terima kasih atas jawaban berkualitas ini.
Stphane
Anda tidak boleh -o yang berarti atau. jadi Anda memangkas pada langkah pertama dan kemudian melupakan semua tentangnya di langkah berikutnya.
mmm
97

Ini adalah satu-satunya yang bekerja untuk saya.

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

Mencari "MyFile" tidak termasuk "Direktori". Beri penekanan pada bintang-bintang *.

DimiDak
sumber
13
Metode ini berfungsi pada macOS sementara jawaban yang diterima tidak. Saya tahu pertanyaan asli adalah untuk Linux.
Xavier Rubio Jansana
5
Perhatikan bahwa Anda dapat menambahkan banyak ! -path '*/Directory/*'ke perintah Anda secara berurutan untuk mengabaikan banyak direktori
Aclwitt
Bekerja pada MacOS tetapi tidak di linux ... dikonfirmasi
Marcello de Sales
Dalam satu- docker containersatunya karya dengansh -c "find..."
Marcello de Sales
@Marcello de Sales Tentu saja ini berfungsi di Linux.
DimiDak
59

Salah satu opsi adalah dengan mengecualikan semua hasil yang berisi nama direktori dengan grep. Sebagai contoh:

find . -name '*.js' | grep -v excludeddir
Joshua
sumber
44
Ini akan membuat pencarian Anda sangat lambat
Dorian
6
Yang ini bekerja untuk saya, yang lain (yang digunakan -prune) - tidak.
Andron
7
Lambat dalam hasil besar, tetapi berguna dalam set yang lebih kecil. Tetapi bagaimana cara mengecualikan beberapa direktori menggunakan grep? Tentu saja dengan cara ini: find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3tetapi mungkin ada beberapa cara grep.
Timo Kähkönen
6
Jika Anda ingin melakukan beberapa greps maka Anda akan lebih baik menulis sebagai ekspresi reguler: egrep -v '(dir1|dir2|dir3)'. Namun, dalam studi kasus khusus ini, akan lebih baik untuk mengecualikan direktori dalam finddirinya sendiri.
Laurence
1
ya, dan Anda tidak perlu tanda kurung dan akan lebih baik menggunakan ^ untuk memastikan cocok dengan nama direktori pada awal string misalnya: temukan. -nama '* .js' | egrep -v "^ \ ./ excludeddir1 | ^ \ ./ excludeddir2"
Sofija
41

Saya lebih suka -notnotasi ... lebih mudah dibaca:

find . -name '*.js' -and -not -path directory
mpapis
sumber
5
Maaf, itu tidak berhasil. Halaman manual untuk findmengatakan: "Untuk mengabaikan direktori dan file di bawahnya, gunakan -prune".
Christian Davén
8
Ini salah. Itu tidak mencegah menemukan memasuki direktori dan melintasi semua file di dalamnya.
GetFree
find . -iname '*' -and -not -path './somePath'tidak mencegahnya memasuki direktori tersebut.
Lemmings19
Ini membantu saya dengan .git path find . -iname '*' -not -path './.git/*'
Mark Shust di M.academy
7
@ membran: Lebih khusus find . -not -path "*/.git*"akan apa yang Anda inginkan.
Ben
20

Gunakan opsi -proune. Jadi, sesuatu seperti:

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

'-Type d -name proc -prune' hanya mencari direktori yang bernama proc untuk dikecualikan.
'-O' adalah operator 'ATAU'.

Drew Frezell
sumber
1
Ini adalah satu-satunya solusi "menemukan" yang bekerja untuk saya. Direktori yang ingin saya kecualikan TIDAK langsung di bawah direktori kerja saat ini.
Lambart
5
Namun, menambahkan -printsampai akhir dapat meningkatkan hasil. find . -type d -name .hg -prune -o -name datamengabaikan isi .hgdirektori (banyak) , tetapi mendaftar .hgdirektori itu sendiri. Dengan -print, itu hanya mendaftar direktori "data" yang saya cari.
Lambart
19

-prunepasti berfungsi dan merupakan jawaban terbaik karena mencegah turun ke direktori yang ingin Anda kecualikan. -not -pathyang masih mencari direktori yang dikecualikan, itu hanya tidak mencetak hasilnya, yang bisa menjadi masalah jika direktori yang dikecualikan dipasang volume jaringan atau Anda tidak memiliki izin.

Bagian yang sulit adalah yang findsangat khusus tentang urutan argumen, jadi jika Anda tidak melakukannya dengan benar, perintah Anda mungkin tidak berfungsi. Urutan argumen umumnya seperti:

find {path} {options} {action}

{path}: Taruh semua argumen terkait jalur terlebih dahulu, seperti . -path './dir1' -prune -o

{options}: Saya paling sukses ketika menempatkan -name, -iname, etcsebagai opsi terakhir dalam grup ini. Misalnya-type f -iname '*.js'

{action}: Anda akan ingin menambahkan -printsaat menggunakan-prune

Berikut ini contoh kerjanya:

# 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
wisbucky
sumber
16

Ini adalah format yang saya gunakan untuk mengecualikan beberapa jalur:

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

Saya menggunakan ini untuk menemukan semua file tidak di jalur ". *":

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
pengguna1882879
sumber
Saya mencoba ini dan masih turun ke direktori, jadi kecepatannya pasti tidak meningkat.
Br.Bill
10

Pendekatan -path -prune juga bekerja dengan wildcard di jalan. Berikut ini adalah pernyataan find yang akan menemukan direktori untuk server git yang melayani beberapa repositori git meninggalkan direktori internal 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 \)  
Wolfgang Fahl
sumber
9

Untuk mengecualikan beberapa direktori:

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

Untuk menambahkan direktori, tambahkan -o -path "./dirname/*":

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

Tetapi mungkin Anda harus menggunakan ekspresi reguler , jika ada banyak direktori untuk dikecualikan.

JBENOIT
sumber
9

Ada banyak jawaban bagus, hanya butuh beberapa waktu untuk memahami untuk setiap elemen perintah dan logika di baliknya.

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

find akan mulai menemukan file dan direktori di direktori saat ini, karenanya find ..

The -opilihan singkatan logis OR dan memisahkan dua bagian dari perintah:

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

Direktori atau file apa pun yang bukan direktori ./misc tidak akan lulus tes pertama -path ./misc. Tetapi mereka akan diuji terhadap ekspresi kedua. Jika nama mereka sesuai dengan pola yang *.txtmereka cetak, karena -printopsi.

Ketika menemukan mencapai direktori ./misc, direktori ini hanya memenuhi ekspresi pertama. Jadi -pruneopsi akan diterapkan untuk itu. Ini memberitahu perintah find untuk tidak menjelajahi direktori itu. Jadi setiap file atau direktori di ./misc bahkan tidak akan dieksplorasi oleh find, tidak akan diuji terhadap bagian kedua dari ekspresi dan tidak akan dicetak.

Istopopoki
sumber
Semua orang punya solusi, tetapi milik Anda menjelaskan yang terbaik. Saya bersikeras agar -nama digunakan terlebih dahulu daripada -path. Penjelasan Anda cukup untuk sampai pada apa yang saya inginkan. Temukan . -nama "* .txt" -print -o -path ./misc -prune
Vendetta V
7

Untuk solusi yang berfungsi (diuji pada Ubuntu 12.04 (Precise Pangolin)) ...

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

akan mencari file MP3 dalam folder dan subfolder saat ini kecuali pada dir1 subfolder.

Menggunakan:

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

... untuk mengecualikan dir1 DAN dir2

james dupin
sumber
Tidak bekerja untuk saya. Tidak ada jawaban di atas. Topi merah.
Tharpa
6

trik yang baik untuk menghindari mencetak direktori yang sudah dipangkas adalah menggunakan -print(berfungsi -execjuga) setelah sisi kanan -orsetelahnya -prune. Sebagai contoh, ...

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

akan mencetak path semua file di bawah direktori saat ini dengan ekstensi `.j2", melewatkan semua direktori tersembunyi. Rapi. Tetapi juga akan mencetak mencetak path lengkap dari setiap direktori yang dilewati, seperti disebutkan di atas. Namun, berikut tidak, ...

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

karena secara logis ada yang tersembunyi -andsetelah -inameoperator dan sebelum -cetak. Ini mengikatnya ke bagian kanan -orklausa karena urutan operasi dan asosiatif boolean. Tetapi dokumen mengatakan ada yang tersembunyi -printjika (atau salah satu sepupunya ... -print0, dll) tidak ditentukan. Jadi mengapa bukan bagian kiri dari -orcetakan? Rupanya (dan saya tidak mengerti ini dari pertama saya membaca halaman manual), itu benar jika tidak ada -print-atau DI -execMANA SAJA, dalam hal ini, -cetak secara logis ditaburkan sedemikian rupa sehingga semuanya dicetak. Jika bahkan SATUprintOperasi-gaya dinyatakan dalam klausa apa pun, semua yang logis disembunyikan pergi dan Anda hanya mendapatkan apa yang Anda tentukan. Sekarang terus terang, saya mungkin lebih suka sebaliknya, tetapi kemudian finddengan hanya operator deskriptif tampaknya tidak melakukan apa-apa, jadi saya kira itu masuk akal seperti itu. Seperti disebutkan di atas, ini semua berfungsi dengan -execbaik, jadi yang berikut ini memberikan ls -ladaftar lengkap untuk setiap file dengan ekstensi yang diinginkan, tetapi tidak mencantumkan level pertama dari setiap direktori tersembunyi, ...

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

Bagi saya (dan orang lain di utas ini), findsintaksis mendapatkan barok cukup cepat, jadi saya selalu melempar parens untuk membuat PASTI saya tahu apa yang mengikat apa, jadi saya biasanya membuat makro untuk tipe-kemampuan dan membentuk semua pernyataan seperti itu. ..

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

Sulit untuk salah dengan mengatur dunia menjadi dua bagian dengan cara ini. Saya harap ini membantu, meskipun tampaknya tidak mungkin bagi siapa pun untuk membaca hingga jawaban ke-30 dan memilihnya, tetapi orang dapat berharap. :-)

cycollins
sumber
5

Anda dapat menggunakan opsi prune untuk mencapai ini. Seperti pada contoh:

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

Atau opsi grep "grep -v" terbalik:

find -iname example.com | grep -v beta

Anda dapat menemukan instruksi dan contoh terperinci di Linux find command mengecualikan direktori dari pencarian .

Siju V
sumber
Solusi grep adalah satu-satunya yang mengecualikan semua direktori dengan nama yang sama. Saat mencoba mengecualikan "node_modules" itu cukup berguna.
bmacnaughton
3
@ bmacnaughton - tidak benar! Saya datang ke sini khusus mencari untuk mengecualikan "node_modules" dan setelah membaca banyak jawaban baik saya menetap di find . -type f -print -o -path "*/node_modules" -prune... menggunakan wildcard ini melompati "node_modules" di tingkat mana pun; menggunakan -printpada alternatif pertama -type f -printhanya membuat bagian cetak, sehingga direktori "node_modules" sendiri tidak terdaftar. (dapat juga dibalikkan find . -path "*/node_modules" -prune -o -type f -print)
Stephen P
apa yang * / lakukan di sana. Apa file persis yang ingin Anda kecualikan. Apakah Anda menggunakannya sebagai wildcard?
Siju V
1
@StephenP, terima kasih telah menunjukkan ini; Saya belajar perbedaan antara menggunakan ./node_modulesdan */node_modulesdari itu. Untuk kasus saya, di mana node_moduleshanya ada di direktori tempat saya memulai pencarian (dan di bawah node_modulesdirektori itu), saya dapat menggunakan find . -type f -print -o -path "./node_modules" -prune karena tidak akan ada node_modulesdirektori di bawah direktori lain.
bmacnaughton
1
@SijuV - dalam direktori tempat saya mencari ada node_modulessubdirektori, tetapi ada juga subdirektori yang memiliki node_modules mereka sendiri ... ./node_moduleshanya menggunakan kecocokan subdirektori di node_modulesbawah direktori saat ini .dan memangkasnya; menggunakan */node_moduleskecocokan dan memangkas direktori pada kedalaman apa pun, karena *sebagai gumpalan cocok dengan awalan jalur terkemuka, seperti ./test5/main/node_modules, tidak hanya ./awalan. Ini *adalah wildcard, tetapi sebagai glob bukan sebagai regex.
Stephen P
5
find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune
Pola Dasar Paul
sumber
Tidak bisa ini bekerja. find ~/Projects -name '*.js' -\! -name 'node_modules' -prunemasih membuka file dengan node_modulesdi jalur mereka
mpen
1
@mpen, Dari stackoverflow.com/questions/4210042/… , saya mengetahui bahwa sintaks yang Anda inginkan adalah find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Nama jalur itu harus sama persis dengan apa yang akan dicetak jika akan mencetak direktori.
PatS
4
find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'

tampaknya bekerja sama dengan

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

dan lebih mudah untuk mengingat IMO.

Funkodebat
sumber
4

TLDR: pahami direktori root Anda dan sesuaikan pencarian Anda dari sana, menggunakan -path <excluded_path> -prune -oopsi. Jangan menyertakan trailing /di ujung jalur yang dikecualikan.

Contoh:

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


Untuk secara efektif menggunakan findSaya percaya bahwa sangat penting untuk memiliki pemahaman yang baik tentang struktur direktori sistem file Anda. Di komputer rumah saya, saya memiliki multi-TB hard drive, dengan sekitar setengah dari konten yang didukung menggunakan rsnapshot(yaitu, rsync). Meskipun mencadangkan ke drive independen (duplikat) secara fisik, drive tersebut dipasang di bawah direktori system root ( /) saya /mnt/Backups/rsnapshot_backups/:

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

The /mnt/Backups/rsnapshot_backups/direktori saat ini menempati ~ 2,9 TB, dengan ~ file 60M dan folder; hanya dengan menelusuri konten-konten tersebut membutuhkan waktu:

## 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

Jadi, kapan saja saya perlu mencari file di /partisi (root) saya, saya harus berurusan dengan (hindari jika mungkin) melintasi partisi backup saya.


CONTOH

Di antara berbagai pendekatan yang disarankan di utas ini ( Cara mengecualikan direktori dalam perintah. ), Saya menemukan bahwa pencarian menggunakan jawaban yang diterima jauh lebih cepat - dengan peringatan.

Solusi 1

Katakanlah saya ingin mencari file sistem libname-server-2.a, tetapi saya tidak ingin mencari melalui rsnapshotbackup saya . Untuk menemukan file sistem dengan cepat, gunakan jalur kecualikan /mnt(mis. Gunakan /mnt, tidak /mnt/, atau /mnt/Backups, ...):

## 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!

... menemukan file itu hanya dalam beberapa detik, sementara ini membutuhkan waktu lebih lama (tampaknya muncul kembali melalui semua direktori "yang dikecualikan"):

## 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

Solusi 2

Solusi lain yang ditawarkan di utas ini ( SO # 4210042 ) juga berkinerja buruk:

## 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

RINGKASAN | KESIMPULAN

Gunakan pendekatan yang diilustrasikan dalam " Solusi 1 "

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

yaitu

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

mencatat bahwa setiap kali Anda menambahkan trailing /ke jalur yang dikecualikan, findperintah kemudian secara rekursif masuk ke /mnt/*direktori (semua itu) - yang dalam kasus saya, karena /mnt/Backups/rsnapshot_backups/*subdirektori, juga menyertakan ~ 2,9 TB file untuk dicari! Dengan tidak menambahkan trailing/ pencarian harus selesai segera (dalam hitungan detik).

"Solusi 2" ( ... -not -path <exclude path> ...) juga tampaknya mencari secara rekursif melalui direktori yang dikecualikan - tidak mengembalikan kecocokan yang dikecualikan, tetapi menghabiskan waktu pencarian yang tidak perlu.


Mencari di dalam rsnapshotcadangan itu:

Untuk menemukan file di salah satu rsnapshotcadangan per jam / harian / mingguan / bulanan saya ):

$ 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)

Tidak termasuk direktori bersarang:

Di sini, saya ingin mengecualikan direktori bersarang, misalnya /mnt/Vancouver/projects/ie/claws/data/*saat mencari dari /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

Selain itu: Menambahkan -printdi akhir perintah akan menekan cetakan direktori yang dikecualikan:

$ 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
Victoria Stuart
sumber
Ini bukan ukuran file yang melambat find, ini adalah jumlah entri direktori yang harus diperiksa. Jadi jauh lebih buruk jika Anda memiliki banyak, banyak file kecil (terutama jika mereka semua ditautkan multipel!) Daripada jika Anda hanya memiliki sedikit file multi-gigabyte.
Toby Speight
@TobySpeight: poin bagus. Saya menyebutkan ukuran ruang pencarian untuk menunjukkan skala, yang juga berisi banyak file. Pencarian cepat root (/) dengan sudo ls -R / | wc -lmenunjukkan ~ 76,5 juta file (kebanyakan didukung kecuali file sistem "non-konfigurasi"); /mnt/Vancouver/dengan ls -R | wc -lmenunjukkan ~ 2,35 juta file; /home/victoria/berisi file 0,668M.
Victoria Stuart
4

Anda juga dapat menggunakan ekspresi reguler untuk memasukkan / mengecualikan beberapa file / dirs pencarian Anda menggunakan sesuatu seperti ini:

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

Ini hanya akan memberi Anda semua file js, vue, css, dll tetapi tidak termasuk semua file di folder node_modulesdan vendor.

supersan
sumber
3

Saya menggunakan finduntuk menyediakan daftar file xgettext, dan ingin menghilangkan direktori tertentu dan isinya. Saya mencoba banyak permutasi -pathdikombinasikan dengan -prunetetapi tidak dapat sepenuhnya mengecualikan direktori yang saya inginkan hilang.

Walaupun saya dapat mengabaikan isi direktori yang saya inginkan diabaikan, findkemudian mengembalikan direktori itu sendiri sebagai salah satu hasil, yang menyebabkanxgettext crash sebagai hasilnya (tidak menerima direktori; hanya file).

Solusi saya adalah cukup gunakan grep -vuntuk melewati direktori yang tidak saya inginkan dalam hasil:

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

Apakah ada argumen untuk finditu akan berhasil 100%, saya tidak bisa mengatakan dengan pasti. Menggunakan grepadalah solusi cepat dan mudah setelah sakit kepala.

Lem19
sumber
3

Tidak ada jawaban sebelumnya yang bagus di Ubuntu. Coba ini:

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

Saya menemukan ini di sini

Sixro
sumber
Saya tidak melihat alasan mengapa salah satu jawaban dengan lebih dari 100 poin tidak dapat digunakan di Ubuntu.
Axel Beckert
mmm mari kita lihat? mungkin karena saya sudah mencoba semuanya?
Sixro
find di mana-mana implementasi yang sama di semua distribusi Linux - yang dari Proyek GNU. Satu-satunya perbedaan mungkin versi. Tetapi perubahan dalam dekade terakhir tidak begitu invasif, kecuali mungkin untuk pencocokan izin.
Axel Beckert
3

Ini cocok untuk saya di Mac:

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

Ini akan mengecualikan vendordan app/cachemengarahkan untuk nama pencarian yang diakhiri dengan php.

jiahut
sumber
Lebih baik letakkan tanda kutip tunggal di sekitar '* .php' atau Anda tidak akan menemukan apa yang Anda cari.
Br.Bill
3

Bagi Anda yang menggunakan UNIX versi lama yang tidak dapat menggunakan -path atau -not

Diuji pada SunOS 5.10 bash 3.2 dan SunOS 5.11 bash 4.4

find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f
JaredTS486
sumber
Dapat melewati lebih dari direktori yang ditentukan.
MUY Belgium
2

Bagaimana cara menggunakan opsi prune-of-find-in-sh adalah jawaban yang sangat baik oleh Laurence Gonsalves tentang cara-prune kerjanya.

Dan inilah solusi generiknya:

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

Untuk menghindari mengetik /path/to/seach/beberapa kali, membungkus finddalam pushd .. popdpasangan.

pushd /path/to/search;                  \
find .                                  \
  -type d                               \
    \( -path ./exclude_me               \
       -o                               \
       -name exclude_me_too_anywhere    \
     \)                                 \
    -prune                              \
  -o                                    \
  -type f -name '*\.js' -print;         \
 popd
go2null
sumber
1
Dari stackoverflow.com/questions/4210042/... , saya belajar bahwa sintaks yang digunakan untuk -pathharus sesuai dengan nama yang menemukan akan mencetak jika ingin mencetak direktori jadi, misalnya, find . -path ./.git -prune -o -printatau find $HOME/foo -path $HOME/foo/.git -prune -o -print Beberapa jawaban hanya mengatakan -path somediryang sayangnya tidak cukup tepat untuk berguna.
PatS
2

Untuk apa yang saya butuhkan ini berfungsi seperti ini, mencari landscape.jpgdi semua server mulai dari root dan mengecualikan pencarian di /vardirektori:

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

find / -maxdepth 1 -type ddaftar semua d irectories di/

grep -v /var mengecualikan `/ var 'dari daftar

xargs -I '{}' find '{}' -name landscape.jpgjalankan perintah apa saja, seperti finddengan setiap direktori / hasil dari daftar

adrianTNT
sumber
Tunggu sebentar, /belum dikecualikan. Anda mungkin perlu sed 1d.
Simba
2

Perintah-perintah berikut ini berfungsi:

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

Jika Anda memiliki masalah dengan find, gunakan -D treeopsi untuk melihat informasi analisis ekspresi.

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

Atau -D all, untuk melihat semua informasi eksekusi.

find -D all . -path ./.git -prune -o -print
EIIPII
sumber
1

Saya menemukan nama fungsi dalam file sumber C mengecualikan * .o dan mengecualikan * .swp dan mengecualikan (bukan file biasa) dan mengecualikan output dir dengan perintah ini:

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

Lebih baik gunakan exectindakan daripada forloop:

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

Ini exec ... '{}' ... '{}' \;akan dieksekusi sekali untuk setiap file yang cocok, menggantikan kawat gigi '{}'dengan nama file saat ini.

Perhatikan bahwa kurung kurawal ditutup dengan tanda kutip tunggal untuk melindunginya dari interpretasi sebagai tanda baca script shell * .


Catatan

* Dari bagian CONTOH di find (GNU findutils) 4.4.2halaman manual

Alberto
sumber
1
Pertanyaan yang sangat lama, tetapi masih ada ruang untuk perbaikan. Saya menemukannya secara kebetulan mencoba memecahkan masalah yang sama, dan tidak ada jawaban yang memuaskan.
Alberto
Saya execsering menggunakan tindakan dan merasa sangat berguna. Saya biasanya menambahkan tanda kutip antara {}seandainya ada spasi di jalur file yang memberikan "{}".
Ludovic Kuty
@ lkuty Saya akan mengedit posting saya untuk mencerminkan komentar Anda, tetapi setelah pengujian cepat (tanpa mengutip, {}tidak berfungsi untuk file dengan spasi putih dalam nama mereka) dan melihat ke halaman manual, tampaknya mengutip hanya diperlukan untuk menghindari mereka disalahartikan sebagai tanda baca skrip shell. Dalam hal ini, Anda akan menggunakan kutipan tunggal:'{}'
Alberto
Saya pikir saya harus menggunakannya untuk membuat cpatau mvatau rm. Saya akan memeriksanya
Ludovic Kuty
1

Saya mencoba perintah di atas, tetapi tidak ada yang menggunakan "-prune" bekerja untuk saya. Akhirnya saya mencoba ini dengan perintah di bawah ini:

find . \( -name "*" \) -prune -a ! -name "directory"
Jolly Liu
sumber