Mengapa nullglob tidak default?

61

Dalam kebanyakan shell nullglobbukan default. Itu berarti, misalnya, jika Anda menjalankan perintah ini

ls *

dalam direktori kosong, itu akan memperluas *glob ke literal *, sebagai gantinya ke daftar argumen kosong. Ada cara untuk mengubah perilaku itu, sehingga *dalam direktori kosong akan mengembalikan daftar argumen kosong, yang akan tampak lebih intuitif.

Jadi, apakah ada alasan mengapa nullglobdinonaktifkan secara default? Jika demikian, apa alasannya?

Dakkaron
sumber
13
Seseorang pernah membuat pilihan buruk yang berubah menjadi "kami selalu melakukannya dengan cara ini". Fenomena yang sangat ada di mana-mana (tidak hanya) di dunia perangkat lunak.
PSkocik
4
Alasan aslinya adalah bahwa opsi nullglob tidak ada saat itu. Jadi untuk menjaga kompatibilitas, itu harus dinonaktifkan secara default.
PM 2Ring
3
Meskipun tidak secara spesifik menyebutkan glob nol, beberapa sejarah tentang globbing disediakan oleh jawaban ini: unix.stackexchange.com/a/136409/22724
StrongBad
4
Saya mendapat kesan beberapa orang berpikir itu seharusnya jelas bahwa nullglob harus diaktifkan secara default. Saya pikir itu tidak jelas. Ekspansi yang terjadi di shell sebelum pemanggilan perintah berarti bahwa ekspansi ke ketiadaan adalah perilaku yang kurang intuitif daripada gumpalan yang tersisa tidak berubah.
kojiro
2
@ Kojiro Kurang intuitif untuk siapa? Siapa pun yang terbiasa dengan cangkang * NIX tahu bahwa itu *adalah gumpalan dan meluas ke semua file yang ada ; bagaimana "intuitif" untuk ada kasus khusus di mana gumpalan direktori kosong "diperluas" ke literal *?
Kyle Strand

Jawaban:

78

The nullglobpilihan (yang BTW adalah zshpenemuan, hanya ditambahkan tahun kemudian bash( 2.0)) tidak akan ideal dalam sejumlah kasus. Dan lsmerupakan contoh yang bagus:

ls *.txt

Atau yang setara lebih benar:

ls -- *.txt

Dengan nullglobakan berjalan lstanpa argumen yang diperlakukan sebagai ls -- .(daftar direktori saat ini) jika tidak ada file yang cocok, yang mungkin lebih buruk daripada memanggil lsdengan *.txtargumen literal sebagai.

Anda akan mengalami masalah serupa dengan sebagian besar utilitas teks:

grep foo *.txt

Akan mencari foodi stdin jika tidak ada txtfile.

Default yang lebih masuk akal, dan salah satu dari csh, tcsh, zsh atau fish 2.3+ (dan dari shell Unix awal) adalah untuk membatalkan perintah sama sekali jika glob tidak cocok.

bash(karena versi 3) memiliki failglobopsi untuk itu (menarik untuk diskusi ini, karena bertentangan dengan ash, AT&T kshatau zsh, bashtidak mendukung cakupan lokal untuk opsi (meskipun itu akan berubah di 4.4), opsi itu ketika diaktifkan secara global tidak merusak beberapa hal. seperti fungsi bash-completion).

Perhatikan bahwa csh dan tcsh sedikit berbeda dari zsh, fishatau bash -O failglobdalam kasus seperti:

ls -- *.txt *.html

Di mana Anda membutuhkan semua gumpalan untuk tidak cocok dengan perintah dibatalkan. Misalnya, jika ada satu file txt dan tidak ada file html, itu menjadi:

ls -- file.txt

Anda bisa mendapatkan perilaku yang dengan zshdengan setopt cshnullglobmeskipun cara yang lebih masuk akal untuk melakukannya di zshakan menggunakan gumpal seperti:

ls -- *.(txt|html)

Di zshdan ksh93, Anda juga dapat menerapkan nullglob berdasarkan per-glob, yang merupakan pendekatan yang lebih waras daripada memodifikasi pengaturan global:

files=(*.txt(N))  # zsh
files=(~(N)*.txt) # ksh93

akan membuat array kosong jika tidak ada txtfile alih-alih gagal perintah dengan kesalahan (atau membuatnya menjadi array dengan satu *.txtargumen literal dengan shell lain).

Versi fishsebelum 2.3 akan berfungsi seperti bash -O nullglobtetapi memberikan peringatan ketika interaktif ketika sebuah gumpal tidak cocok. Sejak 2.3, ini berfungsi seperti zshkecuali untuk gumpalan yang digunakan dalam for, setatau count.

Sekarang, pada catatan sejarah, perilaku itu benar-benar rusak oleh kulit Bourne. Dalam versi Unix sebelumnya, globbing dilakukan melalui /etc/globhelper dan helper itu berperilaku seperti csh: itu akan gagal perintah jika tidak ada gumpalan yang cocok dengan file apa pun dan menghapus gumpalan dengan tidak ada kecocokan sebaliknya.

Jadi situasi kita hari ini adalah karena keputusan buruk yang dibuat di kulit Bourne.

Perhatikan bahwa shell Bourne (dan shell C) datang dengan fitur Unix baru lainnya: lingkungan. Itu berarti ekspansi variabel (pendahulunya hanya memiliki $1,, $2... parameter posisi). Shell Bourne juga memperkenalkan substitusi perintah.

Keputusan desain lain yang buruk dari shell Bourne adalah untuk melakukan globbing (dan pemisahan) pada ekspansi variabel dan substitusi perintah (mungkin untuk kompatibilitas mundur dengan shell Thompson di mana echo $1masih akan memohon /etc/globjika $1berisi wildcard (itu lebih seperti ekspansi makro pra-prosesor di sana, seperti dalam nilai yang diperluas diurai lagi sebagai kode shell)).

Kegagalan gumpalan yang tidak cocok akan berarti misalnya bahwa:

pattern='a.*b'
grep $pattern file

akan gagal perintah (kecuali ada beberapa a.whateverbfile di direktori saat ini). csh(yang juga melakukan globbing pada ekspansi variabel) tidak gagal perintah dalam kasus itu (dan saya berpendapat itu lebih baik daripada meninggalkan bug aktif di sana, bahkan jika itu tidak sebagus tidak melakukan globbing sama seperti di zsh).

Stéphane Chazelas
sumber
Ada masalah kegunaan lain: nullglobmuncul untuk menghancurkan penyelesaian tab (menekan tombol tab tidak melakukan apa-apa saat diaktifkan).
Kyle Strand
1
Dimungkinkan untuk menggunakan nullglob pada basis per glob dengan bash - walaupun sintaksinya tidak seanggun zshfiles=$(shopt -s nullglob;echo *.txt)
Jon Nalley
2
@ JonNalley, yang menyimpan rangkuman (dengan spasi) dari nama file (dengan kemungkinan transformasi dengan xpg_echo) menjadi variabel skalar . Anda akan perlu sesuatu seperti readarray -td '' files < <(shopt -s nullglob; printf '%s\0' *.txt)dengan bash4.4 atau di atas atau (shopt -s nullglob; printf '%s\0' *.txt) | xargs -r0 cmddengan GNU xargsuntuk itu dapat digunakan sama sekali dengan nama file sewenang-wenang. Atau, masih dengan bash4.4, gunakan fungsi pembantu yang menggunakan local -(disalin dari abu 25 tahun kemudian) untuk lingkup lokal untuk opsi.
Stéphane Chazelas