Sejarah Bash globbing

11

Apakah ada alasan historis mengapa Bash "globbing" dan ekspresi reguler tidak identik? Sebagai contoh, saya percaya bahwa di Bash [1-2]*cocok dengan apa pun yang dimulai dengan 1 atau 2 diikuti oleh yang lain, sedangkan sebagai ekspresi reguler [1-2]*hanya akan cocok dengan urutan 1s dan 2s. Script Bash dan REGEX foo saya berdua sangat lemah dan saya sering mengalami masalah yang terkait dengan perbedaan-perbedaan ini yang membuat saya penasaran mengapa mereka berbeda.

StrongBad
sumber
3
Apakah Anda mempertimbangkan melakukan rm -- ^[^.].*\.txt$bukan rm -- *.txt?
Stéphane Chazelas
1
Sebagian besar Q Anda disentuh di utas ini dari lwn: lwn.net/Articles/96687
slm
Ada perintah yang beroperasi pada nama file dan ambil regexp. Misalnya menemukan,, find . -regex ".*\.txt$" | xargs rm --atau renameuntuk mengganti nama file (itu seduntuk nama file), berhati-hatilah beberapa sistem memiliki yang berbeda rename.
ctrl-alt-delor
@ Richard, saya ^[^.].*\.txt$harus memperhitungkan pengabaian file dot. Perhatikan bahwa -regexadalah ekstensi GNU, beberapa shell seperti ksh93 atau zsh dapat menggabungkan regexps dalam gumpalan mereka (coba misalnya: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas
2
Bash itu mengikuti praktik yang ada dengan sangat hati-hati sambil menghindari perubahan dan ekstensi yang tidak kompatibel yang tak dapat didamaikan adalah salah satu kekuatan terbesarnya.
ormaaj

Jawaban:

12

bashawalnya dirancang pada akhir 80-an sebagai tiruan sebagian kshdengan beberapa fitur interaktif dari csh / tcsh.

Asal usul globbing harus ditemukan dalam cangkang-cangkang sebelumnya yang menjadi dasarnya.

kshsendiri merupakan perpanjangan dari shell Bourne. Shell Bourne itu sendiri (pertama kali dirilis pada tahun 1979 di Unix V7) adalah implementasi bersih dari awal, tetapi tidak sepenuhnya berangkat dari shell Thompson (shell V1 -> V6) dan memasukkan fitur dari shell Mashey.

Secara khusus, argumen perintah masih dipisahkan oleh kosong, |sekarang operator pipa baru tetapi ^masih didukung sebagai alternatif (dan juga menjelaskan mengapa Anda melakukannya [!a-z]dan tidak [^a-z]), $1masih argumen pertama untuk skrip dan backslash masih karakter pelarian . Begitu banyak operator regexp ( ^\|$) memiliki makna tersendiri di shell.

Shell Thompson mengandalkan utilitas eksternal untuk globbing. Ketika shditemukan tanda kutip *, [atau ?s dalam perintah, itu akan menjalankan perintah glob.

rm *.txt

akan berakhir menjalankan glob sebagai:

["glob", "rm", "*.txt"]

dan glob akan berakhir berjalan rmdengan daftar file yang cocok dengan pola itu.

grep a.\*b *.txt

akan berjalan globsebagai:

["glob", "grep", "a.\252b", "*.txt"]

Di *atas telah dikutip dengan menetapkan bit ke-8 pada karakter itu, mencegah globmemperlakukannya sebagai wildcard. globkemudian akan menghapus bit itu sebelum memanggil grep.

Untuk melakukan hal yang sama dengan regexps, itu seharusnya:

regexp rm '\.txt$'

Atau:

regexp rm '^[^.].*\.txt$'

untuk mengecualikan file-file dot.

Kebutuhan untuk melarikan diri dari operator karena mereka menggandakan sebagai karakter khusus shell, fakta bahwa ., umum dalam nama file adalah operator regexp membuatnya sangat tidak sesuai untuk mencocokkan nama file dan rumit untuk pemula. Dalam kebanyakan kasus, semua yang Anda butuhkan adalah wildcard yang dapat menggantikan satu ( ?) atau nomor ( *) karakter apa pun.

Sekarang, cangkang yang berbeda menambahkan operator globbing yang berbeda. Saat ini, ksh dan zsh gumpalan (dan sampai batas tertentu bash -O extglobyang mengimplementasikan himpunan bagian dari gumpalan ksh) secara fungsional setara dengan regexps dengan sintaks yang kurang rumit untuk digunakan dengan nama file dan sintaksis shell saat ini. Misalnya, dalam zsh(dengan ekstensi extendedglob), Anda dapat melakukan:

echo a#.txt

jika Anda ingin (tidak mungkin) mencocokkan nama file yang terdiri dari urutan adiikuti oleh .txt. Lebih mudah daripada echo (^a*\.txt$)(di sini menggunakan kawat gigi sebagai cara untuk mengisolasi operator regex dari operator shell yang bisa menjadi salah satu cara shell bisa mengatasinya).

echo (foo|bar|<1-20>).(#i)mpg

Untuk file mpg (tidak sensitif huruf besar) yang nama dasarnya adalah foo, bar atau angka desimal dari 1 hingga 20 ...

ksh93sekarang juga dapat memasukkan regexps (dasar, diperpanjang, perl-like atau "augmented") dalam gumpalannya (meskipun cukup buggy) dan bahkan menyediakan alat untuk mengkonversi antara glob dan regexp ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

untuk mencocokkan file-file txt (non-tersembunyi) dengan E- xtended regular expressions, case- i nsensitive.

Stéphane Chazelas
sumber
Tulisan keren! Anda sebenarnya tidak dapat menggunakan ~(opt:pat)untuk salah satu opsi huruf besar. Mungkin print -r -- ~(Ei).*\.txt$. Menempatkan pola di dalamnya tampaknya hanya berguna untuk menghindari keharusan beralih opsi lalu mematikan untuk bagian dari pola. Anehnya, Anda dapat mencampur-dan-mencocokkan beberapa bahasa pola dalam glob yang sama. ~(Ki)*.~(E)txt$setara. (Pada akhirnya semuanya baru saja dikonversi menjadi regex dan diteruskan ke mesin regex libast secara internal).
ormaaj
@ormaaj, ~(Ei:.*\.txt)bekerja untuk saya bahkan dengan versi 15 tahun seperti ksh93 o +.
Stéphane Chazelas
Bekerja dengan salah satu biner uji tersimpan saya juga (2014-12-24), tapi saya ingat mengalami masalah dengan itu. Hal-hal selalu rusak secara acak dan diperbaiki lagi antara setiap versi kembali ketika ksh masih dikembangkan secara komersial. Saya ingat kode pencocokan pola menjadi salah satu area yang rapuh.
ormaaj
@ormaaj, satu yang berbeda di antara ~(E)xdan ~(E:x)adalah bahwa yang terakhir berlabuh (cocok xhanya saat yang pertama cocok dengan apa pun yang mengandung x), yang mungkin merupakan jenis masalah yang Anda temui (gunakan ~(-lr)~(E:x)untuk menghapus jangkar, ~(E-lr:x)tidak akan dilakukan). Bagaimanapun, saya setuju itu cukup buggy, bahkan dalam versi terbaru.
Stéphane Chazelas
9

Bahasa reguler diperkenalkan oleh Kleene pada tahun 1956. Makalah seminalis tidak memiliki notasi modern penuh untuk ekspresi reguler, tetapi itu memperkenalkan "bintang Kleen": yang A*berarti "sejumlah pengulangan A". Pada dekade berikutnya, beberapa notasi standar lebih atau kurang muncul, khususnya .untuk karakter sewenang-wenang dan ?berarti bahwa karakter sebelumnya adalah opsional.

Notasi Bash globbing berasal dari globperintah yang diperkenalkan sepanjang jalan kembali ke Unix v1 pada tahun 1971. Pada saat itu, globbing dilakukan oleh program terpisah; kemudian dipindahkan ke shell. globPerintah awal ?harus berarti "karakter apa saja" dan *berarti "urutan karakter apa saja". Saya tidak tahu mengapa karakter dipilih; ?cukup intuitif, dan *mungkin terinspirasi dari ekspresi reguler.

Globbing tidak dimaksudkan untuk menjadi umum seperti ekspresi reguler, dan ekspresi reguler tidak terlalu luas pada saat itu, jadi tidak ada panggilan untuk menyatukan konsep. Sejak awal, ada ketidaksesuaian sintaksis, dengan ?, .dan *memaknai hal-hal yang berbeda dalam pola nama file dan dalam ekspresi reguler.

Kerang modern seperti bash berkembang pada pola gumpalan, tapi itu adalah evolusi bertahap yang menjaga kompatibilitas ke belakang. Ksh88 (versi 1988 dari shell Korn ) memperkenalkan sintaksis diperpanjang untuk pola shell, yang tidak mungkin sintaksis yang sama seperti ekspresi reguler biasa tetapi sangat terinspirasi olehnya: *(PATTERN)berarti sejumlah pengulangan PATTERN, @(PATTERN1|PATTERN2)berarti " PATTERN1atau PATTERN2", dll.

Versi modern dari bash (sejak 2.02) mendukung pola ksh88 yang diperluas, jika Anda menerbitkannya shopt -s extglobterlebih dahulu.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Pernahkah Bash tidak mendukung extglobs? Sejauh yang saya ketahui Bash, zsh, dan {pd, m} ksh telah mendukung gumpalan yang sama persis seperti yang didokumentasikan dalam manual ksh88 sejak awal. Ksh hingga hari ini bahkan tidak memiliki opsi untuk menonaktifkan penjumlahan glob "extended", dan ksh93 adalah satu-satunya dari banyak grup yang memiliki ekstensi di luar yang dimiliki ksh88.
ormaaj
2
@ormaaj Ksh88 memperpanjang gumpalan dan extglobopsi diperkenalkan di bash 2.02 di suatu tempat sekitar tahun 1998. Zsh diperoleh ksh_globdalam seri 3.1 di suatu tempat di sekitar waktu yang sama. Zsh memiliki banyak ekstensi globbing sendiri (beberapa memerlukan extended_globopsi).
Gilles 'SANGAT berhenti menjadi jahat'
Saya melihat. Jadi sebenarnya sudah cukup terlambat untuk membenarkan kebutuhan akan suatu opsi. (Saya pikir defaultnya sudah tidak ada gunanya hari ini tetapi, menarik.)
ormaaj
1
@ormaaj, Perhatikan bahwa bashsebaliknya ksh, extglob membuat bash non-POSIX compliant karena tidak dinonaktifkan dalam variabel. Dalam ksh, var='@(*)'; echo $varmemperluas ke semua nama file dalam direktori saat ini yang dimulai dengan @(dan diakhiri )sebagai POSIX membutuhkan sementara di bash -O extglobdalamnya memperluas ke semua file. (masih, orang mungkin menganggap perilaku bash lebih masuk akal di sini (dan perilaku ksh cukup menyebalkan ketika Anda ingin memiliki pola dalam variabel)). Sintaks glob sangat canggung karena itu (kompatibilitas POSIX / Bourne). Bandingkan dengan zsh extended globs.
Stéphane Chazelas
@ StéphaneChazelas Itu semua benar, dan saya suka bagaimana ksh agak pintar tentang hal itu. Ini jarang masuk untuk bermain kecuali kecuali benar-benar dibatasi untuk POSIX. Dengan hampir setiap penggunaan untuk peletakan kata digantikan oleh fitur yang lebih baik, dan menyimpan pola dalam variabel menjadi gangguan ekstrem karena Anda harus mengosongkan IFS, nonaktifkan ekspansi penjepit di mana pun kecuali bash. Saya pikir itu masih mustahil untuk sepenuhnya aman dengan pola yang tersimpan. Masalah pelarian lama ini tidak pernah benar-benar diselesaikan misalnya.
ormaaj
1

Alasan historis: YA. Referensi:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Hanya untuk menunjukkan perbedaan, berikut adalah contoh yang bagus dan mudah: a*

  • shell globbing: makna adalah, karakter pertama adalah adan kemudian apapun (a, ab, abca ...)
  • regex: artinya adalah, nol atau lebih pengulangan karakter a(a, aa, aaa ...)

Saya siap setuju bahwa perbedaan makna ini sangat membingungkan bagi pengguna baru.

Globbing mungkin lebih mudah dipahami oleh pendatang baru, tetapi juga konstruksi yang kurang kuat.

fgeorgatos
sumber