Setelah beberapa penelitian yang sangat cepat, tampaknya Bash adalah bahasa Turing-lengkap .
Saya bertanya-tanya, mengapa Bash digunakan hampir secara eksklusif untuk menulis skrip yang relatif sederhana? Karena Bash shell hadir dengan Linux, Anda dapat menjalankan skrip shell tanpa interpreter atau kompiler eksternal, seperti yang diperlukan untuk bahasa komputer populer lainnya. Ini adalah keuntungan besar, yang dapat mengimbangi mediokritas bahasa itu sendiri dalam beberapa kasus.
Jadi, apakah ada batasan seberapa rumit program semacam itu bisa didapat? Apakah Bash murni digunakan untuk menulis program yang kompleks? Apakah mungkin untuk menulis, misalnya, file kompresor / decompressor di Bash murni? Kompiler? Gim video sederhana?
Apakah ini jarang digunakan hanya karena hanya ada alat debugging yang sangat terbatas?
sumber
sh
Scriptconfigure
yang digunakan sebagai bagian dari proses membangun untuk un banyak * x paket tidak 'relatif sederhana'.m4
makro yang luas .configure
skrip juga lambat, melakukan banyak pekerjaan yang tidak berguna dan telah menjadi subyek beberapa olok-olok lucu. Tentu saja shell dapat digunakan untuk program besar, tetapi sekali lagi orang juga telah membuat komputer dari Game of Life dan Minecraft Conway , dan ada juga bahasa pemrograman seperti Brainf ** k dan Hexagony . Rupanya beberapa orang hanya suka membangun sesuatu dari atom yang sangat kecil dan membingungkan. Anda bahkan dapat menjual game dengan ide itu ...Jawaban:
Konsep kelengkapan Turing sepenuhnya terpisah dari banyak konsep lain yang berguna dalam bahasa untuk pemrograman dalam skala besar : kegunaan, ekspresif, pemahaman, kecepatan, dll.
Jika Turing-kelengkapan adalah semua yang kami butuhkan, kami tidak akan memiliki bahasa pemrograman sama sekali , bahkan bahasa assembly . Pemrogram komputer semua hanya akan menulis dalam kode mesin , karena CPU kami juga Turing-lengkap.
Skrip shell besar dan kompleks - seperti
configure
skrip yang dihasilkan oleh GNU Autoconf - tidak lazim karena berbagai alasan:Hingga relatif baru, Anda tidak dapat mengandalkan memiliki shell yang kompatibel dengan POSIX di mana-mana .
Banyak sistem, terutama yang lebih tua, secara teknis memiliki cangkang kompatibel POSIX di suatu tempat pada sistem, tetapi mungkin tidak berada di lokasi yang dapat diprediksi seperti
/bin/sh
. Jika Anda menulis skrip shell dan harus dijalankan pada banyak sistem yang berbeda, lalu bagaimana Anda menulis baris shebang ? Salah satu opsi adalah melanjutkan dan menggunakan/bin/sh
, tetapi memilih untuk membatasi diri Anda sendiri ke dialek kulit Bourne pra-POSIX jika itu dijalankan pada sistem seperti itu.Pra-POSIX Shell Bourne bahkan tidak memiliki aritmatika internal; Anda harus memanggil
expr
ataubc
menyelesaikannya.Bahkan dengan shell POSIX, Anda kehilangan array asosiatif dan fitur lain yang kami harapkan akan ditemukan dalam bahasa scripting Unix sejak Perl pertama kali menjadi populer di awal 1990-an .
Fakta sejarah itu berarti ada tradisi selama puluhan tahun mengabaikan banyak fitur kuat dalam penerjemah skrip shell keluarga Bourne murni karena Anda tidak dapat mengandalkannya di mana-mana.
Ini masih berlanjut hingga hari ini, pada kenyataannya: Bash tidak mendapatkan array asosiatif sampai versi 4 , tetapi Anda mungkin akan terkejut betapa banyak sistem yang masih digunakan didasarkan pada Bash 3. Apple masih mengirimkan Bash 3 dengan macOS pada 2017 - tampaknya untuk alasan lisensi - dan server Unix / Linux sering menjalankan semua tetapi tidak tersentuh dalam produksi untuk waktu yang sangat lama, sehingga Anda mungkin memiliki sistem lama yang stabil masih menjalankan Bash 3, seperti kotak CentOS 5. Jika Anda memiliki sistem seperti itu di lingkungan Anda, Anda tidak dapat menggunakan array asosiatif dalam skrip shell yang harus dijalankan pada mereka.
Jika jawaban Anda untuk masalah itu adalah bahwa Anda hanya menulis skrip shell untuk sistem "modern", Anda kemudian harus mengatasi kenyataan bahwa titik referensi umum terakhir untuk sebagian besar shell Unix adalah standar shell POSIX , yang sebagian besar tidak berubah karena itu diperkenalkan pada tahun 1989. Ada banyak cangkang berbeda berdasarkan standar itu, tetapi mereka semua memiliki tingkat yang berbeda dari standar itu. Untuk mengambil array asosiatif lagi,
bash
,zsh
, danksh93
semua memiliki fitur itu, tapi ada beberapa yang tidak kompatibel implementasi. Pilihan Anda, maka, adalah hanya menggunakan Bash, atau hanya menggunakan Zsh, atau hanya menggunakanksh93
.Jika jawaban Anda untuk masalah itu adalah, "jadi instal saja Bash 4," atau
ksh93
, atau apa pun, lalu mengapa tidak "hanya" menginstal Perl atau Python atau Ruby saja? Itu tidak dapat diterima dalam banyak kasus; masalah bawaan.Tidak ada modul bahasa scripting shell Bourne keluarga yang mendukung .
Yang paling dekat dengan sistem modul dalam skrip shell adalah
.
perintah - aliassource
dalam varian cangkang Bourne yang lebih modern - yang gagal pada beberapa level relatif terhadap sistem modul yang tepat, yang paling mendasar di antaranya adalah namespacing .Terlepas dari bahasa pemrograman, pemahaman manusia mulai ditandai ketika setiap file dalam program keseluruhan yang lebih besar melebihi beberapa ribu baris. Alasan utama kami menyusun program besar menjadi banyak file adalah agar kami dapat mengabstraksikan kontennya menjadi satu atau dua kalimat paling banyak. File A adalah pengurai baris perintah, file B adalah jaringan I / O pump, file C adalah shim antara library Z dan program lainnya, dll. Ketika satu-satunya metode Anda untuk merakit banyak file ke dalam satu program adalah inklusi teks , Anda membatasi seberapa besar program Anda dapat tumbuh secara wajar.
Sebagai perbandingan, akan seperti jika bahasa pemrograman C tidak memiliki linker, hanya
#include
pernyataan. Dialek C-lite seperti itu tidak membutuhkan kata kunci sepertiextern
ataustatic
. Fitur-fitur itu ada untuk memungkinkan modularitas.POSIX tidak mendefinisikan cara untuk lingkup variabel ke fungsi skrip shell tunggal, apalagi ke file.
Ini secara efektif membuat semua variabel global , yang lagi-lagi merusak modularitas dan kompabilitas.
Ada solusi untuk ini dalam shell post-POSIX - tentu saja
bash
,ksh93
danzsh
setidaknya - tetapi itu hanya membawa Anda kembali ke poin 1 di atas.Anda dapat melihat efek dari ini dalam panduan gaya pada penulisan makro GNU Autoconf, di mana mereka merekomendasikan Anda mengawali nama variabel dengan nama makro itu sendiri, yang mengarah ke nama variabel yang sangat panjang murni untuk mengurangi kemungkinan tabrakan untuk diterima dekat nol.
Bahkan C lebih baik dalam skor ini, satu mil. Tidak hanya sebagian besar program C ditulis terutama dengan variabel fungsi-lokal, C juga mendukung pelingkupan blok, yang memungkinkan beberapa blok dalam fungsi tunggal untuk menggunakan kembali nama variabel tanpa kontaminasi silang.
Bahasa pemrograman Shell tidak memiliki perpustakaan standar.
Dimungkinkan untuk berpendapat bahwa pustaka standar bahasa scripting shell adalah isinya
PATH
, tetapi itu hanya mengatakan bahwa untuk menyelesaikan segala sesuatu yang konsekuensinya, skrip shell harus memanggil seluruh program lain, mungkin yang ditulis dalam bahasa yang lebih kuat untuk mulai dengan.Juga tidak ada arsip pustaka utilitas shell yang banyak digunakan seperti Perl CPAN . Tanpa perpustakaan besar yang tersedia kode utilitas pihak ketiga, seorang programmer harus menulis lebih banyak kode dengan tangan, jadi dia kurang produktif.
Bahkan mengabaikan fakta bahwa sebagian besar skrip shell bergantung pada program eksternal yang biasanya ditulis dalam C untuk menyelesaikan sesuatu yang berguna, ada overhead dari semua
pipe()
→fork()
→exec()
rantai panggilan tersebut. Pola itu cukup efisien di Unix, dibandingkan dengan IPC dan proses peluncuran pada OS lain, tetapi di sini secara efektif menggantikan apa yang akan Anda lakukan dengan panggilan subrutin dalam bahasa scripting lain, yang masih jauh lebih efisien. Itu menempatkan batas yang serius pada batas atas kecepatan eksekusi skrip shell.Skrip Shell memiliki sedikit kemampuan bawaan untuk meningkatkan kinerjanya melalui eksekusi paralel.
Shell Bourne punya
&
,wait
dan pipeline untuk ini, tapi itu sebagian besar hanya berguna untuk menyusun beberapa program, bukan untuk mencapai paralelisme CPU atau I / O. Anda tidak mungkin dapat mematok inti atau menjenuhkan array RAID hanya dengan skrip shell, dan jika Anda melakukannya, Anda mungkin bisa mencapai kinerja yang jauh lebih tinggi dalam bahasa lain.Jaringan pipa khususnya adalah cara yang lemah untuk meningkatkan kinerja melalui eksekusi paralel. Itu hanya memungkinkan dua program berjalan secara paralel, dan salah satu dari dua kemungkinan akan diblokir pada I / O ke atau dari yang lain pada suatu titik waktu tertentu.
Ada cara-cara jaman akhir di sekitar ini, seperti
xargs -P
dan GNUparallel
, tetapi ini hanya beralih ke poin 4 di atas.Dengan tidak adanya kemampuan bawaan untuk mengambil keuntungan penuh dari sistem multi-prosesor, skrip shell akan selalu lebih lambat daripada program yang ditulis dengan baik dalam bahasa yang dapat menggunakan semua prosesor dalam sistem. Untuk mengambil
configure
contoh skrip Autoconf GNU itu lagi, menggandakan jumlah inti dalam sistem tidak akan banyak membantu meningkatkan kecepatannya.Bahasa skrip shell tidak memiliki pointer atau referensi .
Ini mencegah Anda dari melakukan banyak hal dengan mudah dilakukan dalam bahasa pemrograman lain.
Untuk satu hal, ketidakmampuan untuk merujuk secara tidak langsung ke struktur data lain dalam memori program berarti Anda terbatas pada struktur data bawaan . Shell Anda mungkin memiliki array asosiatif , tetapi bagaimana penerapannya? Ada beberapa kemungkinan, masing-masing dengan pengorbanan yang berbeda: pohon merah-hitam , pohon AVL , dan tabel hash adalah yang paling umum, tetapi ada yang lain. Jika Anda memerlukan serangkaian pengorbanan yang berbeda, Anda buntu, karena tanpa referensi, Anda tidak memiliki cara untuk memutarbalikkan banyak jenis struktur data tingkat lanjut. Anda terjebak dengan apa yang diberikan kepada Anda.
Atau, mungkin Anda membutuhkan struktur data yang bahkan tidak memiliki alternatif yang memadai yang dibangun ke dalam juru bahasa skrip shell Anda, seperti grafik asiklik terarah , yang mungkin Anda perlukan untuk memodelkan grafik dependensi . Saya telah memprogram selama beberapa dekade, dan satu-satunya cara saya bisa memikirkan untuk melakukan itu dalam skrip shell adalah dengan menyalahgunakan sistem file , menggunakan symlinks sebagai referensi palsu. Itulah jenis solusi yang Anda dapatkan ketika Anda hanya mengandalkan kelengkapan Turing, yang tidak memberi tahu Anda apakah solusi itu elegan, cepat, atau mudah dimengerti.
Struktur data lanjutan hanyalah satu penggunaan untuk pointer dan referensi. Ada tumpukan aplikasi lain untuk mereka , yang tidak bisa dilakukan dengan mudah dalam bahasa skrip shell keluarga Bourne.
Saya bisa terus dan terus, tetapi saya pikir Anda mengerti maksudnya di sini. Sederhananya, ada banyak bahasa pemrograman yang lebih kuat untuk sistem tipe Unix.
Tentu, dan itulah mengapa GNU Autoconf menggunakan subset sengaja dari keluarga Bourne bahasa skrip shell untuk
configure
output skripnya: sehinggaconfigure
skripnya akan berjalan cukup banyak di mana-mana.Anda mungkin tidak akan menemukan kelompok orang percaya yang lebih besar dalam kegunaan menulis dalam dialek kulit Bourne yang sangat portabel daripada pengembang GNU Autoconf, namun kreasi mereka sendiri ditulis terutama dalam Perl, ditambah beberapa
m4
, dan hanya sedikit shell naskah; hanya keluaran Autoconf yang merupakan skrip shell Bourne murni. Jika itu tidak menimbulkan pertanyaan tentang seberapa berguna konsep "Bourne everywhere", saya tidak tahu apa yang akan terjadi.Secara teknis, tidak, seperti yang disarankan pengamatan kelengkapan Turing Anda.
Tapi itu tidak sama dengan mengatakan bahwa skrip shell yang besar dan sewenang-wenang itu menyenangkan untuk ditulis, mudah di-debug, atau dieksekusi dengan cepat.
"Murni" Bash, tanpa ada panggilan untuk hal-hal di
PATH
? Kompresor mungkin dapat dilakukan dengan menggunakanecho
dan melepaskan urutan hex, tetapi akan cukup menyakitkan untuk dilakukan. Dekompresor mungkin tidak dapat ditulis seperti itu karena ketidakmampuan untuk menangani data biner dalam shell . Anda akhirnya memanggilod
dan menerjemahkan data biner ke format teks, cara asli shell dalam menangani data.Setelah Anda mulai berbicara tentang menggunakan shell scripting seperti yang dimaksudkan, seperti lem untuk mendorong program lain di
PATH
, pintu terbuka, karena sekarang Anda hanya terbatas pada apa yang dapat dilakukan dalam bahasa pemrograman lain, yang berarti Anda tidak memiliki batasan sama sekali. Sebuah shell script yang mendapat semua kekuatannya dengan menelepon ke program lain diPATH
tidak berlari secepat program monolitik yang ditulis dalam bahasa yang lebih kuat, tetapi tidak berjalan.Dan itu intinya. Jika Anda membutuhkan program untuk menjalankan cepat, atau jika perlu kuat sendiri daripada meminjam kekuatan dari orang lain, Anda tidak menulisnya dalam shell.
Berikut Tetris di shell . Game seperti lainnya tersedia, jika Anda mencari.
Saya akan menempatkan dukungan alat debugging turun sekitar 20 tempat pada daftar fitur yang diperlukan untuk mendukung pemrograman dalam ukuran besar. Banyak programmer yang lebih mengandalkan
printf()
debugging daripada debugger yang tepat, terlepas dari bahasa.Dalam shell, Anda memiliki
echo
danset -x
, yang bersama-sama cukup untuk men-debug banyak masalah besar.sumber
&
Anda dapat menjalankan proses secara paralel. Anda dapatwait
menyelesaikan proses anak. Anda dapat mengatur jaringan pipa, dan jaringan pipa yang lebih kompleks menggunakan pipa bernama. Yang paling penting, mudah untuk melakukan pemrosesan paralel dengan cara yang benar, dengan kode boilerplate yang sangat sedikit dan menghindari risiko dan kesulitan multi-threading shared-memory.Kita bisa berjalan atau berenang di mana saja, jadi mengapa kita repot dengan sepeda, mobil, kereta api, kapal, pesawat terbang dan kendaraan lain? Tentu, berjalan atau berenang bisa melelahkan, tetapi ada keuntungan besar karena tidak membutuhkan peralatan tambahan.
Untuk satu hal, meskipun bash adalah Turing-complete, itu tidak pandai memanipulasi data selain bilangan bulat (tidak terlalu besar), string, (satu dimensi) array string, dan peta yang terbatas dari string ke string. Jenis data lain membutuhkan pengkodean yang merepotkan, yang membuatnya sulit untuk menulis program dan sering kali memaksakan kinerja yang tidak cukup baik dalam praktiknya. Misalnya, operasi floating-point di bash sulit dan lambat.
Selain itu, bash memiliki sedikit cara berinteraksi dengan lingkungannya. Ini dapat menjalankan proses, dapat melakukan beberapa jenis akses file sederhana (melalui pengalihan), dan hanya itu. Bash juga memiliki klien jaringan sisi klien. Bash dapat memancarkan null byte dengan cukup mudah (
printf \\0
) tetapi tidak mem-parsing null byte dalam inputnya, yang membuatnya tidak cocok untuk membaca data biner. Bash tidak dapat langsung melakukan hal-hal lain: ia harus memanggil program eksternal untuk itu. Dan itu tidak masalah: cangkang dirancang untuk tujuan utama menjalankan program eksternal! Kerang adalah bahasa lem untuk menggabungkan program bersama. Tetapi jika Anda menjalankan program eksternal, itu berarti program itu harus tersedia - dan kemudian Anda mengurangi keuntungan portabilitas:).Bash tidak memiliki fitur apa pun yang membuatnya lebih mudah untuk menulis program yang kuat
set -e
. Itu tidak memiliki jenis (berguna) ruang nama, modul, atau struktur data bersarang. Bug adalah kesulitan nomor satu dalam pemrograman; sementara kemudahan menulis program bebas bug tidak selalu merupakan faktor penentu dalam memilih bahasa, peringkat bash buruk pada hitungan itu. Bash juga mendapat peringkat buruk pada kinerja ketika melakukan hal-hal selain menggabungkan program bersama.Untuk waktu yang lama bash tidak berjalan di Windows, dan bahkan hari ini bash tidak ada di instalasi Windows default, dan itu tidak berjalan sepenuhnya secara native (bahkan di WSL) dalam arti bahwa bash tidak memiliki antarmuka untuk Fitur asli Windows. Bash tidak berjalan di iOS dan tidak diinstal secara default di Android. Jadi, kecuali Anda sedang menulis aplikasi khusus Unix, bash sama sekali tidak portabel.
Membutuhkan kompiler bukan masalah untuk portabilitas. Kompiler berjalan di mesin pengembang. Membutuhkan perpustakaan juru bahasa atau pihak ketiga bisa menjadi masalah, tetapi di Linux itu adalah masalah yang dipecahkan melalui paket distribusi, dan di bawah Windows, Android dan iOS, orang-orang biasanya menggabungkan komponen pihak ketiga dalam paket aplikasi mereka. Jadi jenis kekhawatiran portabilitas yang Anda pikirkan bukan masalah praktis untuk aplikasi run-of-the-mill.
Jawaban saya berlaku untuk cangkang selain bash. Beberapa detail bervariasi dari shell ke shell tetapi ide umumnya sama.
sumber
Beberapa alasan untuk tidak menggunakan skrip shell untuk program besar, lepas dari kepala saya:
mkdir
atau secaragrep
internal.zsh
memiliki beberapa dukungan. Ini juga karena antarmuka untuk program eksternal sebagian besar berbasis teks, dan\0
digunakan sebagai pemisah.bash -c ...
ataussh -c ...
)sumber