Ekspansi dengan * .txt di shell tidak berfungsi jika tidak ada file .txt

10

Saya bermain-main dengan ekspansi, dan saya memperhatikan perilaku yang aneh. Saya mencoba melakukan:

echo ./*.txt

Dan saya tidak punya file .txt di direktori saya saat ini. Output yang saya dapatkan adalah:

./*.txt

Saya hanya ingin tahu: Mengapa saya mendapatkan ini? Saya berharap tidak mendapatkan hasil.

PS: Ketika saya punya .txtfile, ekspansi itu ditafsirkan dengan benar. Dengan kata lain, katakan saya punya file,, smthn.txtgema sebenarnya bergema current_directory/smthn.txt.

Pengembara yang Tidak Tahu
sumber

Jawaban:

15

Beradaptasi dari halaman manual bash shell,

bash memindai setiap kata untuk karakter *,?, dan [. Jika salah satu karakter ini muncul, maka kata tersebut dianggap sebagai pola, dan diganti dengan daftar nama file yang diurutkan berdasarkan abjad yang cocok dengan pola tersebut. Jika tidak ada nama file yang cocok ditemukan, dan opsi shell nullglob tidak diaktifkan, kata dibiarkan tidak berubah. Jika opsi nullglob diatur, dan tidak ada kecocokan yang ditemukan, kata tersebut dihapus.

Dalam hal ini saya menganggap nullglob tidak diaktifkan, jadi kata tersebut dibiarkan tidak berubah - maka output yang Anda lihat.

ColinB
sumber
2
Anda dapat mengubah perilaku sebagai berikut: shopt -s nullglobakan menghasilkan string kosong untuk pola yang tidak cocok dan shopt -u nullglob(pengaturan standar) akan menghasilkan pola itu sendiri.
PerlDuck
13

Saya berharap tidak mendapatkan hasil.

Jika nullglobdefault, banyak perintah akan berperilaku tidak terduga, karena itu (mungkin sayangnya) perintah yang memperlakukan kasus nol argumen nama file dengan cara yang berbeda secara kualitatif dari kasus satu atau lebih argumen nama file.

Misalkan Anda telah mengaktifkan nullglob( shopt -s nullglob) dan Anda berada di direktori di mana tidak ada file yang cocok *.txt. Maka *.txtmemang akan berkembang menjadi nol - bukan bidang kosong, tapi tidak ada bidang sama sekali - seperti yang Anda harapkan. Tetapi itu akan memiliki hasil ini:

  • ls *.txtakan mencantumkan semua file di direktori saat ini (kecuali file tersembunyi), karena itulah yang lsterjadi ketika Anda tidak memberikan argumen nama file apa pun.
  • cat *.txtakan membaca dari input standar , karena ketika cattidak memiliki argumen nama file, seolah-olah Anda berlari cat -. Jika berjalan secara interaktif, ia menunggu input. Banyak perintah berperilaku seperti ini.
  • cp *.txt dest/akan gagal dengan kesalahan cp: missing destination file operand after 'dest/'. Ini bukan bencana, tetapi membingungkan dan sangat berbeda dari keberhasilan diam-diam yang mungkin diinginkan.
  • file *.txt, dan berbagai program lain tanpa perilaku khusus untuk argumen argumen tanpa nama file, akan tetap gagal dengan pesan kesalahan atau penggunaan saat tidak ada yang diteruskan.
  • Bahkan kasus-kasus yang secara intuitif terasa seperti harus bekerja sering kali tidak akan berhasil. printf 'Got file: "%s"\n' *.txtakan mencetak Got file: ""bukan apa-apa.
  • Kegagalan disengaja mengutip kejadian *, ?dan [yang tidak dimaksudkan untuk diperluas oleh shell akan lebih sering menghasilkan hasil yang jelas salah, tapi cara-cara yang mungkin sulit untuk mencari tahu. Misalnya, jika tidak ada nama file di direktori saat ini dimulai gedit, maka apt list gedit*(di mana apt list 'gedit*'dimaksudkan) akan menjadi adil apt listdan daftar semua paket yang tersedia.

Jadi ada baiknya Anda tidak mendapatkan perilaku ini tanpa memintanya. Mungkin situasi praktis paling umum yang sebenarnya disederhanakan nullglobadalah for f in *.txt. Lihat juga pertanyaan ini (yang terkait dengan jawaban Sergiy Kolodyazhnyy ).

Pertanyaan yang lebih sulit untuk dijawab adalah mengapa - failglobdi mana kesalahan ekspansi memiliki glob yang tidak cocok dengan file apa pun - bukanlah default di bash. Saya percaya jawaban Sergiy Kolodyazhnyy menangkap alasan ini bahkan tanpa mengatasinya secara langsung. Mempertahankan gumpalan yang tidak meluas tanpa menghasilkan kesalahan ekspansi adalah (mungkin sayangnya) perilaku standar, dan juga merupakan perilaku tradisional, dan dengan demikian diharapkan. Meskipun bash tidak berusaha untuk sepenuhnya mematuhi POSIX kecuali jika dipanggil dengan nama shatau melewati --posixopsi, banyak pilihan desainnya bahkan ketika tidak dalam mode POSIX mengikuti POSIX secara langsung. Mereka harus memilih beberapa perilaku, dan ada kelemahan yang terkait dengan bertentangan dengan harapan pengguna.


Saya pikir ini adalah aspek yang paling tidak berpengaruh secara historis dari masalah ini jadi saya menyimpannya untuk yang terakhir ... tetapi perlu disebutkan bahwa ada sesuatu yang sedikit aneh secara konseptual tentang nullglobperilaku.

nullglobkelihatannya anggun pada mulanya karena, secara sintaksis , ini memperlakukan kasus nol file yang cocok tidak berbeda dari kasus satu, dua, atau nomor lainnya. Perintah yang kami jalankan, yang gumpalan meluas ke argumen, tidak cenderung memperlakukannya sama, seperti yang dijelaskan di atas. Tapi secara sintaksis ini setidaknya terasa benar, yang saya pikir adalah motivasi untuk pertanyaan Anda.

Namun, ada lagi, ketidakkonsistenan yang lebih halus yang nullglobtidak diatasi - yang benar-benar menguatkan. Kasus zero globbing karakter ("wildcard") diperlakukan sangat berbeda dari satu, atau dua, atau nomor lainnya. Misalnya, dengan shopt -s nullglob, jika ab?d?ftidak cocok dengan file apa pun, itu dihapus; jika ab?dtidak cocok dengan file apa pun, itu dihapus; tetapi jika abtidak cocok dengan file apa pun (yaitu, jika tidak ada file yang namanya persis ab) itu masih belum dihapus. Tentu saja, itu akan menjadi bencana jika dihapus, karena mungkin tidak dimaksudkan untuk merujuk ke file yang ada di direktori saat ini sama sekali; bahkan mungkin tidak merujuk ke file. Tetapi ini masih menghilangkan harapan untuk konsistensi total.

Tiga perilaku yang disediakan oleh bash - standar untuk memperlakukan gumpalan yang tidak cocok dengan file apa pun seolah-olah itu bukan gumpalan dan meneruskannya tidak diperluas, perilaku yang Anda harapkan untuk memperlakukannya (jika Anda akan memaafkan pergantian frase yang aneh ini) sebagai menandakan semua nol file yang cocok ( nullglob), dan perilaku aman untuk menganggapnya kesalahan ( failglob) - semua mewakili pendekatan yang berbeda terhadap ambiguitas yang melekat dalam shell tidak dapat mengetahui apakah ada kata tertentu yang dimaksudkan untuk menjadi nama file. Shell melakukan ekspansi tanpa mengetahui bagaimana perintah tertentu yang Anda panggil dengannya akan memperlakukan argumen mereka.

Ini adalah salah satu dari banyak contoh pemisahan keprihatinan . Dalam sistem yang desainnya mengikuti filosofi Unix, setiap bagian dimaksudkan untuk melakukan satu hal dan melakukannya dengan baik . Shell memproses teks menjadi perintah dan argumen dan menjalankan perintah itu, yang sebagian besar di luar shell itu sendiri. Ini cenderung jauh lebih bagus dan lebih fleksibel daripada sistem di mana perintah eksternal sendiri bertanggung jawab untuk melakukan transformasi tersebut (seperti dengan prosesor perintah tradisional di DOS dan Windows). Tapi kadang-kadang ada kerugiannya.

Eliah Kagan
sumber
Rupanya, bash-4.3.39 (2) tidak punya failglob. Jadi tidak bisa default karena tidak selalu didukung.
Ruslan
6

Alasan utamanya adalah karena ini adalah perilaku standar yang ditentukan oleh POSIX - standar yang mencakup bahasa perintah shell dan antara lain pencocokan pola (shell seperti bash, dashshell - default Ubuntu /bin/sh, dan kshmengikuti standar ini). Dari bagian 2.13.3 Pola yang Digunakan untuk Ekspansi Nama File :

Jika pola tidak cocok dengan nama file atau nama path yang ada, string pola harus dibiarkan tidak berubah.

Ini tentu saja memiliki efek samping - mencocokkan nama file yang mungkin secara harfiah *.txt. The nullglobpilihan di bashdan zshdapat membantu: jika opsi yang diaktifkan melalui shopt -s nullglob(dan itu tidak diaktifkan secara default yang berlaku untuk pertanyaan ini), maka globstar akan diperluas ke string kosong ketika tidak ada nama file yang cocok. ksh93memiliki mekanisme pencocokan pola canggih sendiri yang mencapai efek yang sama~(N)*.txt

Lihat juga Mengapa nullglob tidak default?

Sergiy Kolodyazhnyy
sumber