Apa yang meluas ke semua file di direktori saat ini secara rekursif?

92

Aku tahu **/*.extmengembang ke semua file dalam semua subdirektori yang cocok *.ext, tapi apa adalah ekspansi yang sama yang mencakup semua file tersebut di saat direktori juga?

Ramon
sumber
4
Pesta saya tidak tertangani **/*.ext. Apakah Anda yakin itu berhasil untuk Anda?
tangens
@tangens Anda harus mengaktifkan globstaropsi sesuai jawaban Dennis.
kenorb

Jawaban:

111

Ini akan bekerja di Bash 4:

ls -l {,**/}*.ext

Agar globe tanda bintang ganda bekerja, globstaropsi perlu disetel (default: aktif):

shopt -s globstar

Dari man bash:

    globstar
                  Jika disetel, pola ** yang digunakan dalam konteks perluasan nama file
                  teks akan cocok dengan file dan nol atau lebih direktori dan
                  subdirektori. Jika polanya hanya diikuti oleh a /, saja
                  direktori dan subdirektori cocok.

Sekarang saya bertanya-tanya apakah mungkin pernah ada bug dalam pemrosesan globstar, karena sekarang dengan hanya menggunakan ls **/*.extsaya mendapatkan hasil yang benar.

Terlepas dari itu, saya melihat analisis yang dilakukan kenorb menggunakan repositori VLC dan menemukan beberapa masalah dengan analisis itu dan dalam jawaban saya tepat di atas:

Perbandingan dengan keluaran findperintah tidak valid karena menentukan -type ftidak menyertakan jenis file lain (khususnya direktori) dan lsperintah yang terdaftar kemungkinan besar melakukannya. Juga, salah satu perintah yang terdaftar, ls -1 {,**/}*.*- yang tampaknya didasarkan pada milik saya di atas, hanya mengeluarkan nama yang menyertakan titik untuk file-file yang ada di subdirektori. Pertanyaan OP dan jawaban saya menyertakan titik karena yang dicari adalah file dengan ekstensi tertentu.

Yang paling penting, bagaimanapun, adalah bahwa ada masalah khusus dalam menggunakan lsperintah dengan pola globstar **. Banyak duplikat muncul karena pola tersebut diperluas oleh Bash ke semua nama file (dan nama direktori) di pohon yang sedang diperiksa. Setelah perluasan, lsperintah mencantumkan masing - masing dan kontennya jika berupa direktori.

Contoh:

Di direktori kami saat ini adalah subdirektori Adan isinya:

A
└── AB
    └── ABC
        ├── ABC1
        ├── ABC2
        └── ABCD
            └── ABCD1

Di pohon itu, **diperluas menjadi "AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1" (7 entri) . Jika Anda melakukan echo **itu, hasil yang tepat akan Anda dapatkan dan setiap entri diwakili satu kali. Namun , jika Anda melakukannya, ls **itu akan menghasilkan daftar dari masing - masing entri tersebut. Jadi pada dasarnya itu ls Adiikuti oleh ls A/AB, dll., Jadi A/ABakan ditampilkan dua kali. Juga, lsakan memisahkan keluaran setiap subdirektori:

...
<blank line>
directory name:
content-item
content-item

Jadi menggunakan wc -lmenghitung semua baris kosong dan judul bagian nama direktori yang membuang hitungan lebih jauh.

Ini adalah alasan lain mengapa Anda tidak perlu mengurails .

Sebagai hasil dari analisis lebih lanjut ini, saya menyarankan untuk tidak menggunakan pola globstar dalam keadaan apa pun selain melakukan iterasi pada pohon file dengan cara ini:

for entry in **
do
    something "$entry"
done

Sebagai perbandingan terakhir, saya menggunakan repositori sumber Bash yang saya miliki dan melakukan ini:

shopt -s globstar dotglob
diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort)
0a1
> .

Saya biasa trmengubah spasi ke baris baru yang hanya berlaku di sini karena tidak ada nama yang menyertakan spasi. Saya biasa sedmenghapus garis depan ./dari setiap baris keluaran find. Saya mengurutkan output findkarena biasanya tidak disortir dan perluasan gumpalan Bash sudah diurutkan. Seperti yang Anda lihat, satu-satunya keluaran dari diffadalah .keluaran direktori saat ini oleh find. Ketika saya melakukannya ls ** | wc -l, hasilnya hampir dua kali lebih banyak baris.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
5
Saya menguji Ubuntu dan Cygwin, dan globstardefaultoff
Steven Penny
12
Jawaban terbaik! tapi saya pikir **/*.extseharusnya sudah cukup. Selain itu, Anda tidak akan memiliki file tersembunyi kecuali Anda shopt -s dotglob.
gniourf_gniourf
2
Untuk menonaktifkan globstar: shopt -u globstar.
kenorb
4
@gniourf_gniourf Pertanyaan sebenarnya meminta untuk menyertakan direktori saat ini secara khusus jadi tidak, **/*.exttidak akan cukup
msciwoj
2
@dotnetCarpenter: Versi Bash yang disertakan dengan MacOS adalah 3.2 yang tidak mendukung globstar, seperti yang Anda ketahui. Tanda bintang ganda diperlakukan sama dengan tanda bintang tunggal. Globstar diperkenalkan di Bash 4.0.
Dijeda sampai pemberitahuan lebih lanjut.
13

Ini akan mencetak semua file di direktori saat ini dan subdirektorinya yang diakhiri dengan '.ext'.

find . -name '*.ext' -print
unutbu
sumber
Meskipun jawaban ini tidak memenuhi "perluasan" yang diminta OP dalam arti yang paling sempit, namun kemungkinan besar akan memberikan hasil yang diinginkan.
Dijeda sampai pemberitahuan lebih lanjut.
7

Anda dapat menggunakan: **/*.*untuk memasukkan semua file secara rekursif (diaktifkan oleh:) shopt -s globstar.

Silakan temukan di bawah pengujian variasi lain dan bagaimana perilakunya.


Menguji folder dengan 3472 file di folder repositori VLC sampel :

(Total file dari 3472 dihitung sebagai per: find . -type f | wc -l)

  • ls -1 **/*.* - mengembalikan 3338
  • ls -1 {,**/}*.*- mengembalikan 3341 (seperti yang diusulkan oleh Dennis )
  • ls -1 {,**/}* - mengembalikan 8265
  • ls -1 **/*- mengembalikan 7817, kecuali file tersembunyi (seperti yang diusulkan oleh Dennis )
  • ls -1 **/{.[^.],}*- mengembalikan 7869 (seperti yang diusulkan oleh Dennis )
  • ls -1 {,**/}.?* - mengembalikan 15855
  • ls -1 {,**/}.* - mengembalikan 20321

Jadi saya pikir metode paling dekat untuk mendaftar semua file secara rekursif adalah contoh pertama ( **/*.*) sesuai komentar gniourf-gniourf (dengan asumsi file memiliki ekstensi yang tepat, atau gunakan yang spesifik), karena contoh kedua memberikan beberapa duplikat lagi seperti di bawah ini :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

dan yang lainnya menghasilkan duplikat lebih lanjut.


Untuk menyertakan file tersembunyi, gunakan: shopt -s dotglob(nonaktifkan oleh shopt -u dotglob). Ini tidak disarankan, karena dapat mempengaruhi perintah seperti mvatau rmdan Anda dapat menghapus file yang salah secara tidak sengaja.

kenorb
sumber
Di terminal Mac dan bash dengan globstar diaktifkan, saya menemukan solusi di atas ( **/*.*) informatif dan bekerja paling baik. Jawaban yang diterima menyebabkan duplikat item di direktori teratas. Pola kerja saya adalah:"${path}"**/*.*
mummybot
Akan menarik untuk mencoba ini dengan opsi lain seperti nullglob dan dotglob
Wilf
4
$ find . -type f

Itu akan mencantumkan semua file di direktori saat ini. Anda kemudian dapat melakukan beberapa perintah lain pada keluaran menggunakan -exec

$find . -type f -exec grep "foo" {} \;

Itu akan menggrep setiap file dari pencarian untuk string "foo".

Amir Afghani
sumber
Sekarang 11 tahun kemudian, mungkin sudah saatnya seseorang menunjukkan bahwa find . -type fberlaku secara rekursif dengan root di direktori saat ini, tidak hanya ke direktori saat ini.
Roger Dahl
4

Mengapa tidak menggunakan perluasan brace untuk menyertakan direktori saat ini juga?

./{*,**/*}.ext

Ekspansi brace terjadi sebelum ekspansi glob, sehingga Anda dapat secara efektif melakukan apa yang Anda inginkan dengan versi bash yang lebih lama, dan dapat mengabaikan penggunaan globstar di versi yang lebih baru.

Selain itu, dianggap praktik yang baik dalam bash untuk memasukkan yang terdepan ./dalam pola glob Anda.

clone206
sumber