Mengapa utilitas wajib POSIX tidak dibangun ke dalam shell?

45

Tujuan dari pertanyaan ini adalah untuk menjawab rasa ingin tahu, bukan untuk memecahkan masalah komputasi tertentu. Pertanyaannya adalah: Mengapa utilitas wajib POSIX tidak umum dibangun ke dalam implementasi shell?

Sebagai contoh, saya memiliki skrip yang pada dasarnya membaca beberapa file teks kecil dan memeriksa apakah mereka diformat dengan benar, tetapi butuh 27 detik untuk menjalankan, pada mesin saya, karena sejumlah besar manipulasi string. Manipulasi string ini membuat ribuan proses baru dengan memanggil berbagai utilitas, karenanya lambat. Saya cukup yakin bahwa jika beberapa utilitas dibangun di, yaitu grep, sed, cut, tr, dan expr, maka script akan berjalan dalam satu detik atau kurang (berdasarkan pengalaman saya di C).

Tampaknya akan ada banyak situasi di mana membangun utilitas ini akan membuat perbedaan antara apakah solusi dalam skrip shell memiliki kinerja yang dapat diterima.

Jelas, ada alasan mengapa itu tidak membuat utilitas ini terintegrasi. Mungkin memiliki satu versi utilitas pada tingkat sistem menghindari memiliki beberapa versi yang tidak sama dari utilitas yang digunakan oleh berbagai shell. Saya benar-benar tidak dapat memikirkan banyak alasan lain untuk menjaga overhead menciptakan begitu banyak proses baru, dan POSIX mendefinisikan cukup tentang utilitas sehingga sepertinya tidak banyak masalah untuk memiliki implementasi yang berbeda, asalkan mereka masing-masing POSIX sesuai. Setidaknya tidak sebesar masalah seperti inefisiensi memiliki begitu banyak proses.

Kyle
sumber
15
Jika 27 detik terlalu lambat Anda bisa menggunakan Python, Perl atau bahasa semi-kompilasi lainnya. Atau tempelkan bagian lambat skrip Anda dan minta perbaikan. Mungkin Anda menggunakan tiga atau empat perintah di mana satu (lebih cepat) mungkin dilakukan.
roaima
8
Kerang tidak benar-benar dibuat untuk tugas-tugas berat, sayangnya dan dunia telah banyak berubah sejak saat-saat ketika Anda bisa pergi hanya dengan skrip shell. Saya setuju dengan roaima - setiap sysadmin yang masuk akal harus menggunakan Python atau Perl dan tidak mengharapkan shell untuk menangani semuanya
Sergiy Kolodyazhnyy
16
Tujuan utama shell adalah untuk menjalankan program lain, bukan memanipulasi data secara langsung. Selama bertahun-tahun, beberapa program eksternal atau fitur yang disediakan oleh mereka (globbing, aritmatika printf, dll) telah dimasukkan ke dalam shell ketika mereka dianggap cukup berguna.
chepner
8
Jika Anda memposting skrip Anda ke codereview.stackexchange.com, saya yakin pengulas dapat membuat beberapa saran untuk mempercepat skrip Anda secara drastis (atau setidaknya menunjukkan mengapa itu harus ditulis dalam Python / etc, bukan shell).
chepner
5
@ Kyle: awkadalah utilitas wajib di POSIX, dan terutama juga cocok (yaitu, sangat cepat) untuk menerapkan skrip yang mungkin Anda menerapkan menggunakan sed, cut, tr, grep, dan exprdalam shell script.
Hewan Nominal

Jawaban:

11

Script shell tidak diharapkan berjalan dengan kecepatan seperti itu. Jika Anda ingin meningkatkan kecepatan skrip Anda, cobalah dalam perl. Jika itu masih terlalu lambat, maka Anda harus pindah ke bahasa yang diketik secara statis seperti java atau c, atau menulis modul C untuk perl yang menjalankan bagian-bagian yang terlalu lambat.

Shell adalah level pertama dari prototyping, jika Anda dapat membuktikan konsep dengan shell, kemudian pindah ke bahasa scripting yang lebih baik yang dapat melakukan lebih banyak batasan memeriksa yang akan mengambil acre of shell.

OS Unix diharapkan mencakup banyak program kecil yang melakukan tugas yang terdefinisi dengan baik yang membentuk gambaran yang lebih besar. Ini adalah hal yang baik karena ia menggabungkan program yang lebih besar. Lihatlah qmail, misalnya dan bandingkan dengan sendmail. qmail dibuat dari banyak program:

http://www.nrg4u.com/qmail/the-big-qmail-picture-103-p1.gif

Mengeksploitasi daemon jaringan tidak akan membantu Anda mengeksploitasi manajer antrian.

Ed Neville
sumber
OP secara khusus TIDAK meminta saran untuk meningkatkan kecepatan kode. Pertanyaannya adalah mengapa utilitas tertentu tidak seperti built-in cdatau pwd.
Stephen C
4
Benar. Jawabannya adalah untuk mengungkapkan perbedaan antara monolitik dan terkotak dan menunjukkan alasan dalam hal ini.
Ed Neville
1
@StephenC cdadalah builtin - dan itu sebenarnya harus, karena mengubah direktori kerja dalam subproses tidak mempengaruhi proses induk.
Jonas
67

Mengapa utilitas wajib POSIX tidak dibangun ke dalam shell?

Karena memenuhi POSIX, sebuah sistem diperlukan 1 untuk menyediakan sebagian besar utilitas sebagai perintah mandiri.

Memiliki mereka builtin akan menyiratkan mereka harus ada di dua lokasi berbeda, di dalam cangkang dan di luarnya. Tentu saja, akan mungkin untuk mengimplementasikan versi eksternal dengan menggunakan pembungkus skrip shell ke builtin, tetapi itu akan merugikan aplikasi non shell yang memanggil utilitas.

Perhatikan bahwa BusyBox mengambil jalur yang Anda sarankan dengan menerapkan banyak perintah secara internal, dan menyediakan varian mandiri menggunakan tautan ke dirinya sendiri. Satu masalah adalah ketika set perintah bisa sangat besar, implementasinya sering merupakan bagian dari standar sehingga tidak sesuai.

Perhatikan juga bahwa setidaknya ksh93, bashdan zshmelangkah lebih jauh dengan menyediakan metode khusus untuk menjalankan shell untuk secara dinamis memuat builtin dari pustaka bersama. Secara teknis, tidak ada yang mencegah semua utilitas POSIX untuk diimplementasikan dan tersedia sebagai builtin.

Akhirnya, memunculkan proses baru telah menjadi operasi yang cukup cepat dengan OS modern. Jika Anda benar-benar terkena masalah kinerja, mungkin ada beberapa perbaikan untuk membuat skrip Anda berjalan lebih cepat.

1 POSIX.1-2008

Namun, semua utilitas standar , termasuk built-in reguler dalam tabel, tetapi tidak built-in khusus yang dijelaskan dalam Utilitas Built-In Khusus, harus diimplementasikan dengan cara sehingga mereka dapat diakses melalui keluarga eksekutif dari berfungsi seperti yang didefinisikan dalam Volume Antarmuka Sistem POSIX.1-2008 dan dapat dipanggil langsung oleh utilitas standar yang memerlukannya (env, find, nice, nohup, waktu, xargs).

Jlliagre
sumber
4
Ini adalah jawaban yang tepat, tetapi saya hanya akan menambahkan bahwa karena antarmuka utilitas ini umumnya melalui stdin / stdout, bahwa meskipun setiap dari mereka juga diimplementasikan sebagai built-in rutin dalam bash, secara efektif masih perlu untuk garpu sendiri dan membuat pipa untuk setiap perintah dalam pipa, jadi hanya akan ada keuntungan marjinal
Chunko
2
@ Chunko Ya. subshell lebih ringan dari proses fork / exec'ed sekalipun.
jlliagre
3
@slebetman Anda kehilangan poin saya. Subshell bukanlah thread atau proses yang dijalankan, terlepas dari apakah mereka berjalan di Linux atau tidak. Subshell hanyalah klon orang tua mereka, dibuat oleh yang fork tidak diikuti oleh exec; forksaat ini merupakan operasi yang sangat ringan dibandingkan dengan exec.
jlliagre
3
Saya mengukur busybox noforkbuiltins memiliki urutan 10x lebih sedikit overhead daripada noexecbuiltin, yang pada gilirannya memiliki ~ 5x lebih sedikit overhead daripada fork + exec dari biner yang terpisah. Definisi sesuai unix.stackexchange.com/a/274322/29483 Sangat menarik bahwa busybox bukan noforksegalanya, meskipun saya tahu beberapa kode busybox dipersingkat dengan tidak membersihkan memori, dan hanya mengandalkan proses yang berumur pendek.
sourcejedi
1
@ jlliagre: Di linux garpu membuat proses. Poin yang mungkin Anda lewatkan adalah bahwa di Linux mereka telah mengoptimalkan proses sedemikian rupa sehingga pengembang telah menentukan bahwa tidak ada lagi keuntungan menciptakan sesuatu yang lebih ringan. Pada dasarnya di linux prosesnya seringan thread.
slebetman
9

Dari manual referensi BASH ,

Perintah builtin diperlukan untuk mengimplementasikan fungsionalitas yang tidak mungkin atau tidak nyaman untuk didapatkan dengan utilitas terpisah.

Seperti yang saya yakin Anda pernah dengar, filosofi UNIX sangat bergantung pada beberapa aplikasi yang semuanya memiliki fungsi terbatas. Setiap built-in memiliki alasan yang sangat bagus mengapa itu built-in. Yang lainnya tidak. Saya pikir kelas yang lebih menarik dari pertanyaan adalah sepanjang baris, "mengapa sebenarnya adalah pwd built-in?"

Stephen C
sumber
2
Dalam satu kata: Modularitas
Peschke
2
/ bin / pwd ada. Saya pikir cdakan menjadi contoh yang lebih baik di sini tentang sesuatu yang tidak mungkin diimplementasikan sebagai alat yang terpisah.
Oskar Skog
1
@OskarSkog Itulah intinya. cdharus dibangun di dalam, pwdtidak. Jadi mengapa bashpelaksana memilih untuk memasukkannya?
Stig Hemmer
1
... yang diliputi oleh unix.stackexchange.com/questions/145479 .
JdeBP
@ StigHemmer /bin/bashmemang ada, tetapi masih builtin. Lihat daftar builtin di gnu.org/software/bash/manual/html_node/…
Stephen C
8

Orang-orang di AT&T bertanya pada diri sendiri hal yang sama

Jika Anda melihat sejarah AT&T Software Toolkit (saat ini tidak aktif di github sejak tim inti pergi), ini persis seperti yang mereka lakukan dengan AT&T Korn shell, alias ksh93.

Kinerja selalu menjadi bagian dari motivasi bagi pengelola ksh93, dan ketika membangun ksh Anda dapat memilih untuk membangun banyak utilitas POSIX umum sebagai perpustakaan yang dimuat secara dinamis. Dengan mengikat perintah-perintah ini ke nama direktori seperti /opt/ast/bin, Anda bisa mengontrol versi perintah mana yang akan digunakan, berdasarkan posisi nama direktori itu di $PATH.

Contoh:

cat chmod chown cksum cmp cp cut date expr fmt head join ln
mkdir mkfifo mktemp mv nl od paste rm tail tr uniq uuencode wc

Daftar lengkapnya dapat ditemukan di repositori github ast .

Perhatikan bahwa sebagian besar alat ast memiliki asalnya sendiri dan akan sangat berbeda dari implementasi gnu yang lebih umum. Tim Riset AT&T mematuhi standar resmi, yang merupakan cara untuk mencapai interoperabilitas ketika Anda tidak dapat berbagi kode.

Henk Langeveld
sumber
6

Jadi kami tidak mengerahkan sumber daya untuk mengoptimalkan alat asli, untuk memenuhi setiap keinginan tertentu. Saya kira yang perlu kita jelaskan adalah berapa banyak keinginan spesifik ini akan membutuhkan biaya untuk diimplementasikan.

POSIX mendefinisikan cukup tentang utilitas sehingga sepertinya tidak banyak masalah untuk memiliki implementasi yang berbeda.

ini asumsi yang buruk :-P.

Sistem Post-POSIX terus menjadi lebih kuat dan nyaman untuk alasan yang baik; sebagai standar setelah-fakta itu tidak pernah benar-benar mengejar ketinggalan.

Ubuntu memulai upaya untuk beralih ke shell POSIX stript-down untuk skrip, untuk mengoptimalkan proses boot init System V lama. Saya tidak mengatakan itu gagal, tetapi itu memicu banyak bug yang harus dibersihkan: "bashisms", skrip yang berjalan di bawah /bin/shsambil mengasumsikan bahwa bashfitur tersedia.

POSIX sh bukan bahasa pemrograman serba guna yang bagus. Tujuan utamanya adalah bekerja dengan baik sebagai shell interaktif. Segera setelah Anda mulai menyimpan perintah ke skrip, ketahuilah bahwa Anda mendekati Turing tarpit . Misalnya tidak mungkin mendeteksi kegagalan di tengah pipa normal . bashditambahkan set -o pipefailuntuk ini, tetapi ini bukan di POSIX.

Fitur serupa yang bermanfaat namun tidak standar disediakan oleh hampir setiap utilitas yang lebih kompleks daripada true.

Untuk kelas tugas yang Anda uraikan, Anda dapat menggambar garis kasar ke Awk, Perl, dan saat ini Python. Berbagai alat diciptakan, dan berkembang secara independen. Apakah Anda berharap mis. GNU Awk dimasukkan ke dalam libutilposixextended?

Saya tidak mengatakan bahwa sekarang kita memiliki satu pendekatan yang secara universal lebih baik, saya bisa mengarahkan Anda. Saya punya titik lemah untuk Python. Awk secara mengejutkan sangat kuat, walaupun saya telah frustrasi dengan beberapa fitur yang khusus untuk GNU Awk. Tetapi intinya adalah bahwa memproses sejumlah besar string secara individual (mungkin dari baris file) bukanlah tujuan desain dari shell POSIX.

sourcejedi
sumber
Saya bertanya-tanya apakah akan ada kesulitan dengan shell yang akan menganggap bahwa perintah yang dijalankan dari daftar lokasi yang dapat dikonfigurasi akan diperlakukan sebagai built-in dalam kasus di mana shell memahami segala sesuatu tentang perintah? Jika skrip melakukan cat -@fnord fooshell harus memutuskan bahwa karena tidak tahu apa -@artinya itu perlu menjalankan perintah yang sebenarnya, tetapi mengingat hanya cat <foo >barshell seharusnya tidak perlu menelurkan proses lain.
supercat
1
@supercat kompleksitas.
sourcejedi
2

Ada juga pertanyaan tentang: Di shell mana Anda akan membuatnya?

Sebagian besar sistem Unix / Linux memiliki banyak cangkang berbeda yang dikembangkan secara independen (sh / bash / korn / ???). Jika Anda membuat alat ke dalam shell, Anda akan berakhir dengan implementasi yang berbeda dari alat-alat ini untuk setiap shell. Ini akan menyebabkan overhead, dan Anda mungkin berakhir dengan berbagai fitur / bug di misalnya grep, tergantung pada shell yang Anda gunakan untuk memintanya.

MTilsted
sumber
zsh cukup populer di beberapa kalangan saat ini. csh / tcsh secara historis memiliki banyak pengikut, tapi saya rasa Anda tidak melihatnya hari ini. Dan ada seikat kerang yang kurang dikenal ...
CVn
Modularitas. Dengan builtin, Anda harus mengkompilasi ulang atau menginstal ulang shell setiap kali perubahan dilakukan ke salah satu builtin itu.
can-ned_food
1

Banyak yang menjawab dengan baik. Saya hanya bermaksud memuji jawaban itu. Saya pikir filosofi UNIX adalah bahwa alat harus melakukan satu hal dan melakukannya dengan baik. Jika seseorang mencoba membuat alat yang mencakup semuanya, itu lebih banyak tempat untuk kegagalan. Membatasi fungsionalitas dengan cara ini membuat seperangkat alat yang dapat diandalkan.

Juga, pertimbangkan, jika fungsionalitas seperti sed atau grep dibangun ke dalam shell, apakah akan mudah untuk dipanggil dari baris perintah saat Anda menginginkannya?

Sebagai penutup, pertimbangkan, beberapa fungsi yang Anda inginkan dalam BASH, adalah dalam BASH . Sebagai contoh, kemampuan untuk pencocokan RE dalam BASH diimplementasikan menggunakan operator biner = ~ (lihat Shell Grammar di Halaman Manual untuk lebih lanjut, khususnya, referensi diskusi tentang konstruksi [[]] untuk jika ). Sebagai contoh yang sangat cepat, katakanlah saya sedang mencari file untuk 2 digit hex:

while read line; do
    if [[ $line =~ 0x[[:xdigit:]]{2} ]]; then
        # do something important with it
    fi
done < input_file.txt

Adapun fungsionalitas seperti sed , lihat di bawah Parameter Ekspansi dalam judul Ekspansi dari halaman manual yang sama. Anda akan melihat banyak hal yang dapat Anda lakukan yang mengingatkan pada sed. Saya paling sering menggunakan sed untuk membuat beberapa jenis subtitusi berubah menjadi teks. Membangun di atas:

# this does not take into account the saving of the substituted text
# it shows only how to do it
while read line; do
    ${line/pattern/substitution}
done < input_file.txt

Pada akhirnya, apakah yang di atas "lebih baik" daripada?

grep -E "[[:xdigit:]]{3}" input_file.txt
sed -e 's/pattern/substitution/' input_file.txt
Andrew Falanga
sumber
Argumen terhadap pertanyaan terakhir dapat ditemukan di bawah unix.stackexchange.com/questions/169716/…
phk
1

Saya kira, ini kecelakaan historis.

Ketika UNIX dibuat pada akhir 1960-an dan awal 1970-an, komputer tidak memiliki memori hampir sebanyak yang mereka lakukan saat ini. Mungkin saja, pada saat itu, untuk mengimplementasikan semua fungsi ini sebagai shell builtin, tetapi karena keterbatasan memori, mereka harus membatasi jumlah fungsionalitas yang dapat mereka terapkan, atau berisiko kehabisan memori dan / atau menukar sampah masalah.

Di sisi lain, dengan mengimplementasikan fungsi yang diberikan sebagai program terpisah, dan dengan membuat dua panggilan sistem yang diperlukan untuk memulai proses baru seringan mungkin, mereka dapat membuat lingkungan skrip yang tidak memiliki masalah tersebut dan yang masih berjalan dengan wajar kecepatan.

Tentu saja, sekali hal-hal itu diimplementasikan sebagai proses yang terpisah, orang akan memulainya dari program yang bukan shell, dan kemudian mereka harus tetap seperti itu, atau tiba-tiba semua perangkat lunak ini mulai rusak.

Itu bukan untuk mengatakan Anda tidak dapat mengimplementasikan beberapa fungsi dua kali, namun, dan memang beberapa shell mengimplementasikan beberapa fungsi yang seharusnya menjadi program eksternal sebagai shell builtin; misalnya, bash mengimplementasikan echoperintah sebagai builtin, tetapi ada juga a/usr/bin/echo

Wouter Verhelst
sumber