Bagaimana shell (bash, misalnya) memperluas pola wildcard?

9

Asumsikan direktori memiliki 100 file dimulai dengan huruf 'a'.

Jika saya melakukan grep <some string> a*dari terminal, bagaimana shell menangani ini?

Apakah ini akan memperluas ekspresi reguler, mendapatkan daftar semua file dimulai dengan a dan grep pada masing-masing file secara berurutan? Atau ada cara lain?

Asumsikan bahwa saya memiliki array nama file di atas yang dimulai dengan 'a'. Apakah akan memakan waktu lebih banyak / lebih sedikit jika saya menulis loop for dan melakukan iterasi sendiri dalam skrip shell atau program ac?

Harithski
sumber
7
BTW, ini globbukan ekspresi reguler. Perbedaan besar.
Aaron D. Marasco

Jawaban:

8

Pertama, nitpick: string seperti a*pada sintaksis shell normal adalah gumpalan, yang bekerja secara berbeda dari ekspresi reguler.

Pada ikhtisar tingkat tinggi, shell interpreter (yaitu bash) memperluas string a*ke daftar setiap nama file yang cocok dengan pola a*. Ini kemudian menjadi bagian dari parameter baris perintah ke satu instance grep(untuk programmer, semua kata yang diperluas pergi sebagai string terpisah ke dalam argvargumen main). grepPerintah tunggal itu kemudian mem-parsing argumen dengan cara apa pun yang dipilihnya, dan terserah untuk grepmenafsirkan argumen tersebut sebagai nama file, opsi, argumen opsi, ekspresi reguler, dll., Dan mengambil tindakan yang sesuai. Semuanya terjadi secara berurutan (AFAIK tidak ada grepimplementasi menggunakan beberapa utas).

Jika Anda menerapkan loop dalam skrip shell untuk melakukan hal yang sama, itu hampir pasti lebih lambat dari proses di atas, karena alasan berikut. Jika Anda menelurkan proses grep baru untuk setiap file, itu pasti akan lebih lambat karena overhead pembuatan proses dikalikan tidak perlu. Jika Anda membuat daftar argumen sendiri di skrip shell dan menggunakan satu contoh grep, apa pun yang Anda lakukan di shell masih akan lebih lambat karena perintah shell harus ditafsirkan (dengan bash), yang menambahkan lapisan kode tambahan, dan Anda akan hanya akan mengimplementasikan kembali apa yang sudah dilakukan bash lebih cepat secara internal dalam kode yang dikompilasi.

Sedangkan untuk menuliskannya sendiri dalam C, Anda mungkin dapat dengan mudah mendapatkan kinerja yang sebanding dengan proses yang dijelaskan dalam paragraf pertama, tetapi tidak mungkin Anda akan dapat mencapai cukup dari peningkatan kinerja dibandingkan implementasi grep / bash saat ini untuk membenarkan waktu. dihabiskan tanpa mempelajari optimalisasi kinerja khusus mesin atau mengorbankan portabilitas. Mungkin Anda bisa mencoba membuat versi yang dapat diparalelkan secara sewenang-wenang grep, tetapi bahkan itu mungkin tidak membantu karena Anda lebih cenderung terikat I / O daripada terikat CPU. Ekspansi dan grep global sudah "cukup cepat" untuk sebagian besar tujuan "normal".

jw013
sumber
Terima kasih atas jawaban yang sangat terperinci. Sebenarnya, saya perlu grep file gzip (masing-masing beberapa GB). Saya punya daftar file-file itu. Saya sekarang memiliki pilihan untuk membangun regex (rumit) untuk mencocokkan file-file itu atau beralih ke daftar yang dikenal dan menjalankan grep pada masing-masing (mudah). Karenanya kekhawatiran tentang kinerja.
harithski
coba zcatdan zgrep; tidak perlu mendekompres mereka satu per satu
jw013
Ya tentu saja. Saya menggunakan zgrep.
harithski
6

Ya, itu akan berkembang ke daftar file dan mengumpankan daftar yang dihasilkan ke grepprogram. Setidaknya itulah yang man bashdikatakan di dalam bagian Perluasan Pathname .

Ada cara lain untuk menggunakan ekspansi dalam kasus sederhana seperti yang Anda sebutkan: tulis grep <some_string> adan sebelum menekan* , tekan ESC. Ini akan memperluas daftar file yang cocok tepat di baris perintah, sehingga Anda dapat memverifikasi daftar itu OK sebelum menekan Enter.

Adapun bagian kedua dari pertanyaan Anda, itu tergantung. Jika Anda bermaksud menulis for-loop yang menjalankan grep pada setiap file secara bergantian, maka itu pasti akan lebih lambat, karena program grep akan dijalankan tidak hanya sekali, tetapi sekali per file. Namun, apa yang penting untuk diingat adalah bahwa ada tertentu batas pada panjang diperluas argumen baris perintah yang dapat Anda gunakan, meskipun biasanya cukup tinggi. Untuk melihatnya, Anda bisa mencoba grep adasdsadf /usr/*/*/* >/dev/null.

rozcietrzewiacz
sumber
2
ESC+*tidak persis sama dengan membiarkan bash diperluas * karena ESC+*akan memasukkan dotfile (nama yang dimulai dengan a .) sedangkan perluasan *tergantung pada dotglob shoptpengaturan. Urutan kunci untuk memperluas dan memasukkan gumpalan adalah C-x *secara default dan memetakan ke perintah readline glob-expand-word.
jw013
1
@ jw013 Terima kasih atas informasinya! Tampaknya tidak mengubah kasus a*ekspansi, tetapi tentu saja penting dalam lingkup yang lebih luas.
rozcietrzewiacz
2
zshCatatan: cukup menekan tombol tab pada parameter yang dapat diperluas (pola glob, brace-expansion, command-substitution, ...) akan memperluasnya.
Stéphane Gimenez
@ jw013 Sebenarnya, saya baru saja menguji C-xpintasan dan tidak memperluas daftar file di sistem saya (menggunakan bash).
rozcietrzewiacz
1
@roz Benar - Saya jarang menggunakannya, hanya ingin menunjukkan perbedaan (agak nitpicky) :). C-x *hanya melakukan gumpalan yang hanya melakukan nama file, tetapi Esc *sebenarnya melakukan lebih banyak karena itu insert-completions, seperti dalam semua penyelesaian yang mungkin. Ini berarti menggunakan Esc *pada baris perintah kosong akan memasukkan nama setiap file yang dapat dieksekusi di Anda $PATH, misalnya.
jw013