Saya menulis skrip bash, dan saya mengeksekusinya tanpa mengompilasinya terlebih dahulu. Itu bekerja dengan sempurna. Itu dapat bekerja dengan atau tanpa izin, tetapi ketika datang ke program C, kita perlu mengkompilasi kode sumber. Mengapa?
Saya menulis skrip bash, dan saya mengeksekusinya tanpa mengompilasinya terlebih dahulu. Itu bekerja dengan sempurna. Itu dapat bekerja dengan atau tanpa izin, tetapi ketika datang ke program C, kita perlu mengkompilasi kode sumber. Mengapa?
Jawaban:
Ini berarti bahwa skrip shell tidak dikompilasi, mereka ditafsirkan: shell menafsirkan skrip satu perintah pada suatu waktu, dan mencari tahu setiap kali bagaimana menjalankan setiap perintah. Itu masuk akal untuk skrip shell karena mereka menghabiskan sebagian besar waktu mereka menjalankan program lain.
Program C di sisi lain biasanya dikompilasi: sebelum mereka dapat dijalankan, kompiler mengubahnya menjadi kode mesin secara keseluruhan, sekali dan untuk semua. Ada juru bahasa C di masa lalu (seperti juru bahasa HiSoft C pada Atari ST) tetapi mereka sangat tidak biasa. Saat ini kompiler C sangat cepat; TCC sangat cepat sehingga Anda dapat menggunakannya untuk membuat "skrip C", dengan #!/usr/bin/tcc -run
shebang, sehingga Anda dapat membuat program C yang berjalan dengan cara yang sama seperti skrip shell (dari perspektif pengguna).
Beberapa bahasa umumnya memiliki juru bahasa dan kompiler: BASIC adalah salah satu contoh yang muncul dalam pikiran.
Anda juga dapat menemukan apa yang disebut kompiler skrip shell tetapi yang saya lihat hanyalah pembungkus yang membingungkan: mereka masih menggunakan shell untuk benar-benar menafsirkan skrip. Seperti yang ditunjukkan mtraceur melalui kompiler skrip shell yang tepat tentu akan mungkin, hanya saja tidak terlalu menarik.
Cara lain untuk berpikir tentang hal ini adalah dengan mempertimbangkan bahwa kemampuan menafsirkan skrip shell adalah perpanjangan dari kemampuan penanganan baris perintahnya, yang secara alami mengarah pada pendekatan yang ditafsirkan. C di sisi lain dirancang untuk menghasilkan biner yang berdiri sendiri; ini mengarah pada pendekatan yang dikompilasi. Bahasa yang biasanya dikompilasi cenderung untuk menumbuhkan penerjemah juga, atau setidaknya parser baris perintah (dikenal sebagai REPLs, loop read-eval-print ; shell itu sendiri adalah REPL).
execve
, open
, close
, read
, write
, dan pipe
syscalls, diselingi dengan beberapa getenv
, setenv
, dan operasi internal hashmap / array (untuk variabel non-diekspor ), dll. Bourne shell dan turunannya juga bukan bahasa pemrograman yang mendapatkan manfaat sebanyak tweak kompiler tingkat rendah seperti pemesanan ulang kode, dll.
Pertimbangkan program berikut:
2 Mars Bars
2 Milks
1 Bread
1 Corn Flakes
Dalam bash
jalan, Anda berkeliling toko mencari bar mars, akhirnya menemukan mereka, lalu berkeliling mencari susu dll. Ini bekerja karena Anda menjalankan program kompleks yang disebut "Pembeli berpengalaman" yang dapat mengenali roti ketika Anda melihat satu dan semua kerumitan belanja lainnya. bash
adalah program yang cukup kompleks.
Atau, Anda dapat menyerahkan daftar belanjaan Anda ke kompiler belanja. Kompilator berpikir sebentar dan memberi Anda daftar baru. Daftar ini PANJANG , tetapi terdiri dari instruksi yang lebih sederhana:
... lots of instructions on how to get to the store, get a shopping cart etc.
move west one aisle.
move north two sections.
move hand to shelf three.
grab object.
move hand to shopping cart.
release object.
... and so on and so forth.
Seperti yang Anda lihat, kompiler tahu persis di mana semuanya ada di toko sehingga seluruh fase "mencari sesuatu" tidak diperlukan.
Ini adalah program yang berdiri sendiri dan tidak perlu "Pembeli berpengalaman" untuk mengeksekusi. Yang dibutuhkan hanyalah manusia dengan "Sistem operasi dasar manusia."
Kembali ke program komputer: bash
"pembelanja berpengalaman" dan dapat mengambil skrip dan lakukan saja tanpa mengkompilasi apa pun. Kompiler AC menghasilkan program yang berdiri sendiri yang tidak lagi membutuhkan bantuan untuk dijalankan.
Baik interpreter dan kompiler memiliki kelebihan dan kekurangan.
Itu semua bermuara pada perbedaan teknis antara bagaimana program yang dapat Anda baca / tulis ketika manusia dikonversi menjadi instruksi mesin yang dipahami komputer Anda - dan berbagai kelebihan dan kekurangan dari masing-masing metode adalah alasan mengapa beberapa bahasa ditulis untuk memerlukan kompiler , dan beberapa ditulis untuk ditafsirkan.
(Catatan: Saya menyederhanakan banyak di sini demi menjawab pertanyaan. Untuk pemahaman yang lebih mendalam, catatan teknis di bagian bawah jawaban saya menguraikan / memperbaiki beberapa penyederhanaan di sini, dan komentar pada jawaban ini memiliki beberapa klarifikasi dan diskusi yang bermanfaat juga ..)
Pada dasarnya ada dua kategori umum bahasa pemrograman:
C berada dalam kategori pertama ( kompiler C menerjemahkan bahasa C ke dalam kode mesin komputer Anda : kode mesin disimpan ke dalam file, dan kemudian ketika Anda menjalankan kode mesin itu, ia melakukan apa yang Anda inginkan).
bash ada di kategori kedua (bash interpreter membaca bahasa bash dan bash interpreter melakukan apa yang Anda inginkan: jadi tidak ada "modul kompiler" per se, penerjemah melakukan interpreting dan mengeksekusi, sedangkan kompiler membaca dan menerjemahkan) .
Anda mungkin telah memperhatikan apa artinya ini:
Dengan C, Anda melakukan langkah "menafsirkan" satu kali , lalu kapan pun Anda perlu menjalankan program, Anda cukup memberi tahu komputer Anda untuk mengeksekusi kode mesin - komputer Anda dapat menjalankannya secara langsung tanpa harus melakukan "pemikiran" tambahan.
Dengan bash, Anda harus melakukan langkah "interpretasikan" setiap kali Anda menjalankan program - komputer Anda menjalankan penerjemah bash, dan penerjemah bash melakukan "pemikiran" tambahan untuk mencari tahu apa yang perlu dilakukan untuk setiap perintah, setiap kali .
Jadi program C membutuhkan lebih banyak CPU, memori, dan waktu untuk persiapan (langkah kompilasi) tetapi lebih sedikit waktu dan kerja untuk dijalankan. program bash membutuhkan lebih sedikit CPU, memori, dan waktu untuk persiapan, tetapi lebih banyak waktu dan kerja untuk dijalankan. Anda mungkin tidak melihat perbedaan-perbedaan ini sebagian besar karena komputer sangat cepat saat ini, tetapi itu membuat perbedaan, dan perbedaan itu bertambah ketika Anda perlu menjalankan program besar atau rumit, atau banyak program kecil.
Selain itu, karena program C dikonversi menjadi kode mesin ("bahasa asli") komputer, Anda tidak dapat mengambil program dan menyalinnya ke komputer lain dengan kode mesin yang berbeda (misalnya, Intel 64-bit ke Intel 32). -bit, atau dari Intel ke ARM atau MIPS atau apa pun). Anda harus meluangkan waktu untuk mengkompilasinya untuk bahasa mesin yang lain lagi . Tapi program bash bisa dipindahkan ke komputer lain yang sudah menginstal bash interpreter, dan itu akan berjalan dengan baik.
Pembuat C sedang menulis sistem operasi dan program lain pada perangkat keras dari beberapa dekade yang lalu, yang agak dibatasi oleh standar modern. Karena berbagai alasan, mengubah program menjadi kode mesin komputer adalah cara terbaik untuk mencapai tujuan itu bagi mereka saat itu. Plus, mereka melakukan pekerjaan yang penting agar kode yang mereka tulis berjalan efisien .
Dan pembuat Bourne shell dan bash menginginkan yang sebaliknya: Mereka ingin menulis program / perintah yang dapat dieksekusi segera - pada baris perintah, di terminal, Anda hanya ingin menulis satu baris, satu perintah, dan memilikinya menjalankan. Dan mereka ingin skrip yang Anda tulis berfungsi di mana saja di mana Anda menginstal / program shell.
Jadi singkatnya, Anda tidak memerlukan kompiler untuk bash tetapi Anda memerlukannya untuk C karena bahasa-bahasa tersebut diubah menjadi tindakan komputer yang berbeda, dan cara melakukan yang berbeda itu dipilih karena bahasa memiliki tujuan yang berbeda.
Anda sebenarnya bisa membuat juru bahasa C , atau bash compiler. Tidak ada yang menghentikan hal itu menjadi mungkin: hanya saja bahasa-bahasa itu dibuat untuk tujuan yang berbeda. Seringkali lebih mudah untuk hanya menulis ulang program dalam bahasa lain, daripada menulis penerjemah atau kompiler yang baik untuk bahasa pemrograman yang kompleks. Terutama ketika bahasa-bahasa itu memiliki hal khusus yang mereka kuasai, dan dirancang dengan cara kerja tertentu. C dirancang untuk dikompilasi, sehingga tidak ada banyak steno praktis yang Anda inginkan dalam shell interaktif, tetapi sangat baik untuk mengekspresikan manipulasi data / memori tingkat rendah yang sangat spesifik dan berinteraksi dengan sistem operasi , yang merupakan tugas yang sering Anda lakukan ketika Anda ingin menulis kode yang dikompilasi secara efisien. Sementara itu, bash sangat pandai menjalankan program lain,
Detail lebih lanjut: Sebenarnya ada bahasa pemrograman yang merupakan campuran dari kedua jenis (mereka menerjemahkan kode sumber "sebagian besar jalan", sehingga mereka dapat melakukan sebagian besar penafsiran / "berpikir" sekali, dan hanya melakukan sedikit saja dari interpretasi / "berpikir" nanti). Java, Python, dan banyak bahasa modern lainnya sebenarnya merupakan campuran seperti itu: mereka mencoba memberi Anda beberapa portabilitas dan / atau manfaat pengembangan cepat dari bahasa yang ditafsirkan, dan beberapa kecepatan bahasa yang dikompilasi. Ada banyak cara yang mungkin untuk menggabungkan pendekatan seperti itu, dan berbagai bahasa melakukannya secara berbeda. Jika Anda ingin mempelajari topik ini, Anda dapat membaca tentang bahasa pemrograman yang dikompilasi menjadi "bytecode" (yang agak mirip dengan mengkompilasi ke dalam "bahasa mesin" buatan Anda sendiri)
Anda bertanya tentang bit eksekusi: sebenarnya, bit yang dapat dieksekusi hanya ada di sana untuk memberi tahu sistem operasi bahwa file itu diizinkan untuk dieksekusi. Saya menduga bahwa satu-satunya alasan skrip bash bekerja untuk Anda tanpa izin eksekusi sebenarnya adalah karena Anda menjalankannya dari dalam bash shell. Biasanya, sistem operasi, ketika diminta untuk mengeksekusi file tanpa set bit eksekusi, hanya akan mengembalikan kesalahan. Tetapi beberapa shell seperti bash akan melihat kesalahan itu, dan membawanya sendiri untuk menjalankan file, dengan meniru langkah-langkah yang biasanya diambil oleh sistem operasi (lihat baris "#!" Di awal file, dan coba untuk mengeksekusi program itu untuk menginterpretasikan file, dengan default sendiri atau /bin/sh
jika tidak ada baris "#!").
Terkadang kompiler sudah diinstal pada sistem Anda, dan kadang-kadang IDE datang dengan kompiler mereka sendiri dan / atau menjalankan kompilasi untuk Anda. Ini mungkin membuat bahasa yang dikompilasi "terasa" seperti bahasa yang tidak dikompilasi untuk digunakan, tetapi perbedaan teknis masih ada.
Bahasa "dikompilasi" tidak harus dikompilasi ke dalam kode mesin, dan keseluruhan kompilasi ini adalah topik itu sendiri. Pada dasarnya, istilah ini digunakan secara luas: sebenarnya dapat merujuk pada beberapa hal. Dalam satu hal tertentu, "kompiler" hanyalah penerjemah dari satu bahasa (biasanya bahasa "tingkat lebih tinggi" yang lebih mudah digunakan manusia) ke bahasa lain (biasanya bahasa "tingkat bawah" yang lebih mudah digunakan oleh komputer - kadang-kadang, tetapi sebenarnya tidak terlalu sering, ini adalah kode mesin). Juga, kadang-kadang ketika orang mengatakan "kompiler" mereka benar-benar berbicara tentang beberapa program bekerja bersama (untuk kompiler C khas, itu sebenarnya empat program: "pra-prosesor", kompiler itu sendiri, "assembler", dan " tautan ").
Bahasa pemrograman / skrip dapat dikompilasi atau ditafsirkan.
Executable yang dikompilasi selalu lebih cepat dan banyak kesalahan dapat dideteksi sebelum dieksekusi.
Bahasa yang ditafsirkan biasanya lebih mudah untuk ditulis dan diadaptasi menjadi kurang ketat dari bahasa yang dikompilasi, dan tidak memerlukan kompilasi yang membuatnya lebih mudah untuk didistribusikan.
Bayangkan bahwa bahasa Inggris bukan bahasa ibu Anda (yang mungkin cukup mudah bagi Anda jika bahasa Inggris bukan bahasa ibu Anda).
Ada 3 cara Anda membaca ini:
Komputer memiliki semacam "bahasa asli" - kombinasi instruksi yang dimengerti prosesor, dan instruksi yang dimengerti sistem operasi (misalnya Windows, Linux, OSX dll). Bahasa ini tidak dapat dibaca oleh manusia.
Bahasa scripting, seperti Bash, biasanya jatuh ke dalam kategori 1 dan 2. Mereka mengambil garis pada suatu waktu, menerjemahkan garis itu dan menjalankannya, kemudian pindah ke baris berikutnya. Di Mac dan Linux, beberapa penerjemah yang berbeda diinstal secara default untuk bahasa yang berbeda, seperti Bash, Python dan Perl. Di Windows, Anda harus menginstalnya sendiri.
Banyak bahasa scripting melakukan sedikit pra-pemrosesan - cobalah untuk mempercepat eksekusi dengan mengkompilasi potongan kode yang akan sering dijalankan atau yang akan memperlambat aplikasi. Beberapa istilah yang mungkin Anda dengar tentang kompilasi Ahead-of-time (AOT) atau Just-in-time (JIT).
Terakhir, bahasa yang dikompilasi - seperti C - menerjemahkan seluruh program sebelum Anda dapat menjalankannya. Ini memiliki keuntungan bahwa terjemahan dapat dilakukan pada mesin yang berbeda untuk eksekusi, jadi ketika Anda memberikan program kepada pengguna, sementara mungkin masih ada bug, beberapa jenis kesalahan sudah dapat dibersihkan. Sama seperti jika Anda memberikan ini kepada penerjemah Anda, dan saya menyebutkan caranya garboola mizene resplunks
, itu mungkin terlihat seperti bahasa Inggris yang valid bagi Anda tetapi penerjemah dapat memberi tahu Anda bahwa saya sedang berbicara omong kosong. Saat Anda menjalankan program yang dikompilasi, ia tidak memerlukan juru bahasa - itu sudah dalam bahasa asli komputer
Namun ada satu kelemahan untuk bahasa yang dikompilasi: Saya menyebutkan bahwa komputer memiliki bahasa asli, terdiri dari fitur-fitur dari perangkat keras dan sistem operasi - yah, jika Anda mengkompilasi program Anda pada Windows, Anda tidak akan mengharapkan program yang dikompilasi untuk berjalan pada sebuah Mac. Beberapa bahasa mengatasi hal ini dengan mengkompilasi ke semacam bahasa setengah jalan - sedikit seperti Pidgin English - dengan cara itu, Anda mendapatkan manfaat dari bahasa yang dikompilasi, serta peningkatan kecepatan yang kecil, tetapi itu berarti Anda harus menggabungkan penerjemah dengan kode Anda (atau gunakan yang sudah diinstal).
Terakhir, IDE Anda mungkin mengkompilasi file untuk Anda, dan dapat memberi tahu Anda tentang kesalahan sebelum Anda menjalankan kode. Terkadang, pengecekan kesalahan ini bisa lebih mendalam daripada yang dilakukan kompiler. Sebuah kompiler akan sering hanya memeriksa sebanyak yang diperlukan sehingga dapat menghasilkan kode asli yang masuk akal. Suatu IDE akan sering menjalankan beberapa pemeriksaan tambahan dan dapat memberi tahu Anda, misalnya, jika Anda telah mendefinisikan variabel dua kali, atau jika Anda telah mengimpor sesuatu yang belum Anda gunakan.
Banyak orang berbicara tentang kompilasi interpretasi vs tetapi saya pikir ini bisa sedikit menyesatkan jika Anda melihat lebih dekat karena beberapa bahasa yang ditafsirkan sebenarnya dikompilasi menjadi bytecode antara sebelum eksekusi.
Pada akhirnya, alasan sebenarnya mengapa program C perlu dikompilasi ke format yang dapat dieksekusi adalah bahwa komputer perlu melakukan banyak pekerjaan untuk mengubah kode dalam file sumber C menjadi sesuatu yang dapat dijalankan sehingga masuk akal untuk menyimpan produk dari semua itu berfungsi menjadi file yang dapat dieksekusi sehingga Anda tidak perlu melakukannya lagi setiap kali Anda ingin menjalankan program Anda.
Di sisi lain, juru bahasa Shell perlu melakukan sedikit pekerjaan untuk mengubah skrip shell menjadi "operasi mesin". Pada dasarnya hanya perlu membaca skrip baris demi baris, membaginya di whitespace, mengatur beberapa pengalihan file dan jalur pipa dan kemudian melakukan fork + exec. Karena overhead parsing dan pemrosesan input tekstual dari skrip shell sangat kecil dibandingkan dengan waktu yang dibutuhkan untuk meluncurkan proses dalam skrip shell, itu akan berlebihan untuk mengkompilasi skrip shell ke format mesin perantara, bukan hanya menafsirkan kode sumber langsung.