find: prune tidak mengabaikan jalur yang ditentukan

13

Saya perlu mengecualikan .gitdari findpencarian saya . Untuk mencapai itu, saya menggunakan -path ./.git -prunesakelar:

$ find . -path ./.git -prune -o \( -type f -o -type l -o -type d \) | grep '.git'
./.git

Namun, meskipun ini melompati isi direktori .git, ia mencantumkan direktori itu sendiri. Ini berfungsi saat saya menambahkan-path ./.git -prune -o -print -a

find . -path ./.git -prune -o -print -a \( -type f -o -type l -o -type d \) | grep '.git'

Mengapa ini perlu? Saya pikir kedua perintah harus memiliki output yang sama. Sintaks kedua cukup jelek.

Martin Vegter
sumber
4
Anda memerlukan -print, jika tidak, implisit -printberlaku untuk seluruh kondisi
Stéphane Chazelas
1
Lihat juga unix.stackexchange.com/q/102191/22565
Stéphane Chazelas

Jawaban:

3

The manhalaman untuk findmemberikan:

-prune True; if the file is a directory, do not  descend  into  it.  If
      -depth  is  given,  false;  no  effect.  Because -delete implies
      -depth, you cannot usefully use -prune and -delete together.

Jadi dalam contoh pertama tidak begitu tidak -path ./.git -prunebenar dan oleh karena itu tindakan default ( -print) tidak akan dipanggil, maka garis tersebut dicetak.

Timo
sumber
22

Saya bingung mengapa direktori pemangkasan dicetak oleh findperintah juga, dan beberapa detail rumit lainnya tentang cara -prunekerjanya, tetapi saya bisa mengetahuinya dengan beberapa contoh.

Untuk menjalankan contoh di bawah ini, buat direktori dan file berikut.

mkdir aa
mkdir bb
touch file1
touch aa/file1
touch bb/file3

Untuk membuat struktur ini:

$ tree

.
├── aa
   └── file1
├── bb
   └── file3
└── file1

Sekarang gunakan find untuk mencari direktori bernama aa. Tidak masalah di sini.

$ find . -type d -name aa
./aa

Cari semua direktori selain aa dan kami mendapatkan direktori saat ini .dan ./bb, yang juga masuk akal.

$ find . -type d ! -name aa
.
./bb

Sejauh ini bagus, tetapi ketika kita menggunakan -prune, temukan mengembalikan direktori yang kita pangkas, yang awalnya membingungkan saya karena saya mengharapkannya untuk mengembalikan semua direktori lain dan bukan yang sedang dipangkas.

$ find . -type d -name aa -prune
./aa

Alasan mengapa ia mengembalikan direktori yang sedang dipangkas dijelaskan, bukan di -prunebagian halaman manual seperti yang ditunjukkan dalam jawaban Timo , tetapi di EXPRESSIONSbagian:

Jika ekspresi tidak mengandung tindakan selain -prune,  -printdilakukan pada semua file yang ekspresinya benar.

yang berarti bahwa, karena ekspresi cocok dengan aanama direktori, maka ekspresi akan bernilai true dan akan dicetak karena find secara implisit menambahkan a -printdi akhir seluruh perintah. Namun, itu tidak akan menambahkan -print, jika Anda sendiri sengaja menambahkan tindakan -o -printke akhir:

find . -type d -name aa -prune -o -print
.
./file1
./bb
./bb/file3

Di sini perintah find TIDAK menambahkan implisit -printlagi, sehingga direktori yang kita pangkas ( aa) tidak akan dicetak.

Jadi akhirnya, jika kita menambahkan klausa yang mencari file dengan pola nama file*setelah -o, maka Anda harus meletakkan -printdi akhir klausa kedua seperti ini:

find . \( -type d -name aa -prune \) -o \( -type f -name 'file*' -print \)
./file1
./bb/file3

Alasan mengapa ini bekerja adalah sama: Jika Anda tidak memasukkan a -printdalam klausa kedua, maka karena tidak ada tindakan selain -prunetindakan, find akan menambahkan secara -printotomatis di THE END perintah, menyebabkan -pruneklausa untuk mencetak direktori pangkas:

find . \( \( -type d -name aa -prune \) -o \( -type f -name 'file*' \) \) -print
./aa
./file1
./bb/file3

Secara umum, Anda perlu menempatkan -printperintah di klausa kedua. Jika Anda meletakkannya di tengah seperti yang dilakukan oleh poster asli, itu tidak akan berfungsi dengan benar karena file yang dipangkas akan segera dicetak dan klausa kedua tidak akan mendapatkan kesempatan untuk memilih file yang diinginkan:

find . \( -type d -name aa -prune -o -print \) -o \( -type f -name 'file*' \)
.
./file1
./bb
./bb/file3

Jadi sayangnya, poster asli mendapat perintah yang salah di atas dengan menempatkannya -printdi tempat yang salah. Mungkin berhasil untuk kasus spesifiknya, tetapi tidak bekerja dalam kasus umum.

Ada ribuan orang yang kesulitan memahami cara -prunekerjanya. The findhalaman manual harus diperbarui untuk mencegah kebingungan di seluruh dunia neverending tentang perintah ini.

Jerry Marbas
sumber
Halaman manual saya (Arch Linux, find-4.6.0) mengatakan, "Jika seluruh ekspresi tidak mengandung tindakan selain -prune atau -print, -print dilakukan pada semua file yang seluruh ekspresi benar." Tetapi berfungsi seperti yang Anda gambarkan.
x-yuri
0

dari manhalaman,

Untuk mengabaikan pohon direktori keseluruhan, gunakan -prune daripada memeriksa setiap file di pohon. Sebagai contoh, untuk melewati direktori `src / emacs 'dan semua file dan direktori di bawahnya, dan mencetak nama-nama file lain yang ditemukan, lakukan sesuatu seperti ini:

find . -path ./src/emacs -prune -o -print

Jadi, mungkin Anda bisa menggunakan:

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

Ini tidak akan mencantumkan .gitdirektori.

untuk spesifik nama file, Anda dapat melakukan ini:

find . -path ./.git -prune , -name filename

Harap perhatikan ,operator koma.

jianyongli
sumber
-1

Inilah alternatif cepat dan kotor:

find | fgrep -v /.git

Saya tidak dapat mengingat findsintaks yang rumit ...

peter.slizik
sumber