Batasi temuan POSIX hingga kedalaman tertentu?

15

Baru-baru ini saya perhatikan bahwa spesifikasi POSIX untukfind tidak termasuk yang -maxdepthutama.

Bagi mereka yang tidak terbiasa dengan itu, tujuan -maxdepthutama adalah untuk membatasi berapa banyak level yang findakan turun. hanya-maxdepth 0 menghasilkan argumen baris perintah yang sedang diproses; hanya akan menangani hasil secara langsung dalam argumen baris perintah, dll.-maxdepth 1

Bagaimana saya bisa mendapatkan perilaku yang setara dengan -maxdepthprimer non-POSIX menggunakan hanya opsi dan alat yang ditentukan POSIX?

(Catatan: Tentu saja saya bisa mendapatkan yang setara -maxdepth 0dengan hanya menggunakan -prunesebagai operan pertama, tetapi itu tidak meluas ke kedalaman lain.)

Wildcard
sumber
@StevenPenny, FreeBSD -depth -2, -depth 1... pendekatan bisa dilihat sebagai lebih baik dari GNU -maxdepth/-mindepth
Stéphane Chazelas
@ StéphaneChazelas, baik - menemukan POSIX harus memiliki satu atau yang lain; selain itu lumpuh
Steven Penny
1
Setidaknya untuk -maxdepth/ -mindepth, ada alternatif yang masuk akal (perhatikan bahwa -pathini adalah tambahan terbaru untuk POSIX). Alternatif untuk -timexyatau -mtime -3m(atau -mmin -3) jauh lebih rumit. Beberapa suka -execdir/ -deletetidak punya alternatif yang dapat diandalkan.
Stéphane Chazelas
2
@StevenPenny, silakan login tiket di austingroupbugs.net untuk memintanya ditambahkan. Saya telah melihat banyak hal ditambahkan tanpa perlu sponsor ketika ada pembenaran yang kuat. Tindakan yang mungkin lebih baik adalah mendapatkan banyak implementasi menambahkannya terlebih dahulu sehingga POSIX hanya perlu menentukan yang ada yang umumnya kurang kontroversial.
Stéphane Chazelas
@ StéphaneChazelas dalam kasus saya, saya akhirnya hanya menamai file secara langsung, tetapi terima kasih; Saya mungkin mengajukan tiket jika ini muncul lagi
Steven Penny

Jawaban:

7

Anda dapat menggunakan -pathuntuk mencocokkan kedalaman yang diberikan dan memangkas di sana. Misalnya

find . -path '*/*/*' -prune -o -type d -print

akan menjadi maxdepth 1, karena *cocok dengan ., */*cocok ./dir1, dan */*/*cocok ./dir1/dir2yang dipangkas. Jika Anda menggunakan direktori awal mutlak Anda perlu menambahkan terkemuka /untuk -pathjuga.

meuh
sumber
Hmmm, gampang-gampang susah. Tidak bisakah Anda hanya menghapus satu lapisan dari /*ujung pola, mengambil -ooperator, dan mendapatkan hasil yang sama?
Wildcard
Tidak, karena *cocok /juga, jadi dir a/b/c/d/eakan pas -path */*, sedih.
meuh
Tetapi a/b/c/d/etidak akan pernah tercapai , karena -pruneakan diterapkan pada a/b....
Wildcard
1
Maaf, saya salah membaca -prunedan -odihapus. Jika Anda menjaga -prunemasalahnya adalah bahwa */*tidak akan cocok dengan apa pun pada tingkat di atas maxdepth, misalnya direktori tunggal a.
meuh
11

@ meuh's pendekatan tidak efisien karena -maxdepth 1pendekatannya masih memungkinkan findmembaca isi direktori di level 1 untuk kemudian mengabaikannya sebaliknya. Ini juga tidak akan berfungsi dengan baik dengan beberapa findimplementasi (termasuk GNU find) jika beberapa nama direktori mengandung urutan byte yang tidak membentuk karakter yang valid di lokal pengguna (seperti untuk nama file dalam pengkodean karakter yang berbeda).

find . \( -name . -o -prune \) -extra-conditions-and-actions

adalah cara yang lebih kanonik untuk mengimplementasikan GNU -maxdepth 1(atau FreeBSD -depth -2).

Meskipun demikian, umumnya -depth 1Anda ingin ( -mindepth 1 -maxdepth 1) karena Anda tidak ingin mempertimbangkan .(kedalaman 0), dan kemudian lebih sederhana:

find . ! -name . -prune -extra-conditions-and-actions

Sebab -maxdepth 2, itu menjadi:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Dan di situlah Anda menjalankan masalah karakter yang tidak valid.

Sebagai contoh, jika Anda memiliki direktori bernama Stéphanetetapi yang édikodekan dalam iso8859-1 (alias latin1) charset (0xe9 byte) seperti yang paling umum di Eropa Barat dan Amerika hingga pertengahan 2000-an, maka 0xe9 byte bukan merupakan karakter yang valid dalam UTF-8. Jadi, di lokal UTF-8, *wildcard (dengan beberapa findimplementasi) tidak akan cocok Stéphanedengan *0 karakter atau lebih dan 0xe9 bukan karakter.

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

My find(ketika output masuk ke terminal) menampilkan byte 0xe9 yang tidak valid seperti di ?atas. Anda dapat melihat bahwa St<0xe9>phane/Chazelasitu bukan pruned.

Anda dapat mengatasinya dengan melakukan:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Tetapi perhatikan bahwa itu memengaruhi semua pengaturan lokal finddan aplikasi apa pun yang dijalankannya (seperti melalui -execpredikat).

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

Sekarang, saya benar-benar mendapatkan -maxdepth 2tetapi perhatikan bagaimana é di Stéphane kedua yang dikodekan dengan benar dalam UTF-8 ditampilkan sebagai ??0xc3 0xa9 byte (dianggap sebagai dua karakter individu yang tidak terdefinisi di C locale) dari pengkodean UTF-8 dari é. tidak dapat dicetak karakter di lokal C.

Dan jika saya telah menambahkan -name '????????', saya akan mendapatkan Stéphane yang salah (yang dikodekan dalam iso8859-1).

Untuk menerapkan jalur yang berubah-ubah ., Anda harus:

find some/dir/. ! -name . -prune ...

untuk -mindepth 1 -maxdepth 1atau:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

untuk -maxdepth 2.

Saya masih akan melakukan:

(cd -P -- "$dir" && find . ...)

Pertama karena itu membuat jalur lebih pendek yang membuatnya kurang mungkin berjalan ke jalur terlalu lama atau daftar arg masalah terlalu lama tetapi juga untuk bekerja di sekitar fakta yang findtidak dapat mendukung argumen jalur sewenang-wenang (kecuali -fdengan FreeBSD find) karena akan tersedak nilai $dirsuka !atau -print...


The -odalam kombinasi dengan negasi adalah trik umum untuk menjalankan dua set independen -condition/ -actiondi find.

Jika Anda ingin menjalankan -action1rapat file -condition1dan secara mandiri -action2rapat file -condition2, Anda tidak bisa melakukan:

find . -condition1 -action1 -condition2 -action2

Seperti -action2hanya akan dijalankan untuk file yang memenuhi kedua kondisi.

Maupun:

find . -contition1 -action1 -o -condition2 -action2

Karena -action2tidak akan dijalankan untuk file yang memenuhi kedua kondisi tersebut.

find . \( ! -condition1 -o -action1 \) -condition2 -action2

berfungsi seperti yang \( ! -condition1 -o -action1 \)akan diselesaikan dengan true untuk setiap file. Itu mengasumsikan -action1adalah tindakan (seperti -prune, -exec ... {} +) yang selalu mengembalikan true . Untuk tindakan seperti -exec ... \;itu dapat mengembalikan false , Anda mungkin ingin menambahkan -o -somethingtempat lain -somethingyang tidak berbahaya tetapi mengembalikan true seperti -truedi GNU findatau -links +0atau -name '*'(meskipun perhatikan masalah tentang karakter yang tidak valid di atas).

Stéphane Chazelas
sumber
1
Suatu hari saya akan bertemu dengan banyak file berbahasa Mandarin dan saya akan sangat senang saya telah membaca banyak jawaban Anda tentang karakter lokal dan karakter yang valid. :)
Wildcard
2
@Wildcard, Anda (dan bahkan orang Cina) lebih cenderung mengalami masalah dengan nama file Inggris, Prancis ... daripada nama file Cina karena nama file Cina lebih sering disandikan dalam UTF-8 daripada nama file skrip alfabet yang umumnya dapat ditutupi oleh charset byte tunggal yang merupakan norma hingga relatif baru-baru ini. Ada charset multi-byte lain untuk menutupi karakter Cina, tetapi saya berharap orang-orang Cina akan beralih ke UTF-8 lebih awal dari orang barat karena rangkaian karakter tersebut memiliki sejumlah masalah buruk. Lihat juga hasil edit sebagai contoh.
Stéphane Chazelas
0

Saya mengalami masalah di mana saya perlu cara untuk membatasi kedalaman saat mencari beberapa jalur (bukan hanya .).

Sebagai contoh:

$ find dir1 dir2 -name myfile -maxdepth 1

Ini membawa saya ke pendekatan alternatif menggunakan -regex. Intinya adalah:

-regex '(<list of paths | delimited>)/<filename>'

Jadi, yang di atas adalah:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

Tanpa nama file:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

Akhirnya, untuk -maxdepth 2perubahan regex ke:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'

Alissa H
sumber
1
Pertanyaan ini menanyakan solusi standar (seperti pada POSIX). Juga -maxdepthakan berfungsi dengan beberapa jalur pencarian.
Kusalananda