Aku tahu **/*.ext
mengembang ke semua file dalam semua subdirektori yang cocok *.ext
, tapi apa adalah ekspansi yang sama yang mencakup semua file tersebut di saat direktori juga?
Jawaban:
Ini akan bekerja di Bash 4:
ls -l {,**/}*.ext
Agar globe tanda bintang ganda bekerja, globstar
opsi 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 **/*.ext
saya 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 find
perintah tidak valid karena menentukan -type f
tidak menyertakan jenis file lain (khususnya direktori) dan ls
perintah 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 ls
perintah 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, ls
perintah mencantumkan masing - masing dan kontennya jika berupa direktori.
Contoh:
Di direktori kami saat ini adalah subdirektori A
dan 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 A
diikuti oleh ls A/AB
, dll., Jadi A/AB
akan ditampilkan dua kali. Juga, ls
akan memisahkan keluaran setiap subdirektori:
...
<blank line>
directory name:
content-item
content-item
Jadi menggunakan wc -l
menghitung 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 tr
mengubah spasi ke baris baru yang hanya berlaku di sini karena tidak ada nama yang menyertakan spasi. Saya biasa sed
menghapus garis depan ./
dari setiap baris keluaran find
. Saya mengurutkan output find
karena biasanya tidak disortir dan perluasan gumpalan Bash sudah diurutkan. Seperti yang Anda lihat, satu-satunya keluaran dari diff
adalah .
keluaran direktori saat ini oleh find
. Ketika saya melakukannya ls ** | wc -l
, hasilnya hampir dua kali lebih banyak baris.
globstar
defaultoff
**/*.ext
seharusnya sudah cukup. Selain itu, Anda tidak akan memiliki file tersembunyi kecuali Anda shopt -s dotglob
.
globstar
: shopt -u globstar
.
**/*.ext
tidak akan cukup
Ini akan mencetak semua file di direktori saat ini dan subdirektorinya yang diakhiri dengan '.ext'.
find . -name '*.ext' -print
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 3338ls -1 {,**/}*.*
- mengembalikan 3341 (seperti yang diusulkan oleh Dennis )ls -1 {,**/}*
- mengembalikan 8265ls -1 **/*
- mengembalikan 7817, kecuali file tersembunyi (seperti yang diusulkan oleh Dennis )ls -1 **/{.[^.],}*
- mengembalikan 7869 (seperti yang diusulkan oleh Dennis )ls -1 {,**/}.?*
- mengembalikan 15855ls -1 {,**/}.*
- mengembalikan 20321Jadi 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 mv
atau rm
dan Anda dapat menghapus file yang salah secara tidak sengaja.
**/*.*
) informatif dan bekerja paling baik. Jawaban yang diterima menyebabkan duplikat item di direktori teratas. Pola kerja saya adalah:"${path}"**/*.*
$ 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".
find . -type f
berlaku secara rekursif dengan root di direktori saat ini, tidak hanya ke direktori saat ini.
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.
**/*.ext
. Apakah Anda yakin itu berhasil untuk Anda?