Bash bir Turing-tam dil gibi görünüyor
Turing tamlığı kavramı , geniş bir programlama için bir dilde yararlı olan diğer birçok kavramdan tamamen ayrıdır : kullanılabilirlik, ifade edilebilirlik, anlaşılabilirlik, hız, vb.
Turing-tamlık biz gerekli tüm olsaydı, biz herhangi programlama dilleri olmazdı hiç , hatta montaj dili . CPU'larımız da Turing-complete olduğundan, bilgisayar programcılarının hepsi sadece makine koduyla yazacaktı .
Bash neden neredeyse sadece nispeten basit komut dosyaları yazmak için kullanılıyor?
configure
GNU Autoconf tarafından üretilen komut dosyaları gibi büyük, karmaşık kabuk komut dosyaları birçok nedenden dolayı atipiktir:
Nispeten yakın zamana kadar, her yerde POSIX uyumlu bir kabuğa sahip olmaya güvenemezdiniz .
Birçok sistemde, özellikle de daha eski sistemlerde, teknik olarak sistemin bir yerinde POSIX uyumlu bir kabuk bulunur, ancak bunun gibi öngörülebilir bir konumda olmayabilir /bin/sh
. Bir kabuk betiği yazıyorsanız ve birçok farklı sistemde çalışması gerekiyorsa, o zaman shebang satırını nasıl yazıyorsunuz ? Bir seçenek, devam etmek ve kullanmaktır /bin/sh
, ancak böyle bir sistemde çalıştırılması durumunda kendinizi POSIX öncesi Bourne mermi lehçesi ile sınırlamayı seçin.
POSIX Öncesi Bourne mermilerinin yerleşik aritmetiği bile yoktur; Bunu yapmak için expr
veya çağırmak zorundasınız bc
.
POSIX kabuğuyla bile, Perl ilk olarak 1990'ların başında popüler hale geldiğinden, Unix komut dosyası dillerinde bulmayı umduğumuz ilişkilendirilebilir dizileri ve diğer özellikleri kaçırıyorsunuz .
Tarihin bu gerçeği, modern Bourne ailesi kabuk senaryo çevirmenlerindeki güçlü özelliklerin çoğunu tamamen göz ardı etme geleneği olduğu anlamına gelir, çünkü onları her yerde bulundurmaya güvenemezsiniz.
Bu, bugün hala devam ediyor: Bash, sürüm 4'e kadar ilişkilendirilebilir diziler alamadı , ancak hala kullanımda olan kaç sistemin Bash 3'e dayandığına şaşırabilirsiniz. Apple, 2017'de hala Bash 3'ü macOS ile gönderiyor - görünüşe göre lisanslama nedenleri - ve Unix / Linux sunucuları genellikle üretimde dokunulmamış olanlar çok uzun süre çalışır, bu nedenle CentOS 5 kutusu gibi hala Bash 3 çalıştıran eski bir sisteminiz olabilir. Ortamınızda bu tür sistemler varsa, üzerinde çalıştırılması gereken kabuk komut dosyalarında ilişkilendirilebilir diziler kullanamazsınız.
Bu soruna cevap size "modern" sistemler için yalnızca yazma kabuk komut, daha sonra en Unix kabukları için son ortak referans noktası olduğu gerçeğini başa çıkmak zorunda olduğu ise POSIX kabuk standart oldu beri büyük ölçüde değişmeden olduğu, Bu standarda dayalı birçok farklı mermi vardır, ancak hepsi bu standarda göre değişen derecelere ayrılmıştır. Yine ilişkilendirilebilir diziler çekmek için bash
, zsh
ve ksh93
bütün bu özelliğe sahip, ancak birden çok uygulama uyumsuzluklar vardır. O halde seçiminiz sadece Bash kullanmak veya sadece Zsh kullanmak veya sadece kullanmaktır ksh93
.
Bu soruna cevabınız "öyleyse sadece Bash 4'ü yükleyin" veya ksh93
ya da her neyse, neden Perl veya Python veya Ruby'yi "sadece" yüklemiyorsunuz? Bu birçok durumda kabul edilemez; varsayılanlar önemlidir.
Bourne ailesi kabuk komut dosyası dillerinin hiçbiri modülleri desteklemez .
Bir kabuk komut dosyasında bir modül sistemine gelebildiğiniz en yakın komut, en temel ad alanı olan uygun bir modül sistemine göre birden fazla düzeyde başarısız olan .
komuttur - source
daha modern Bourne kabuk varyantlarında - .
Programlama dilinden bağımsız olarak, daha büyük bir programdaki herhangi bir dosya birkaç bin satırı aştığında insan anlayışı işaretlenmeye başlar. Büyük programları birçok dosyada yapılandırmamızın nedeni, içeriğini en fazla bir veya iki cümleyle özetleyebilmemizdir. A dosyası komut satırı ayrıştırıcısı, B dosyası ağ G / Ç pompası, C dosyası Z kitaplığı ve programın geri kalanı vb. Arasındaki şimdir. Birçok dosyayı tek bir programa birleştirmek için tek yöntem metin içerme olduğunda , programlarınızın ne kadar büyüyebileceğine bir sınır koyarsınız.
Karşılaştırma için, C programlama dilinin hiçbir bağlayıcısı değil, sadece #include
ifadeleri vardı. Böyle bir C-lite lehçesi extern
veya gibi anahtar kelimelere ihtiyaç duymaz static
. Bu özellikler modülerliğe izin vermek için mevcuttur.
POSIX , değişkenleri tek bir kabuk komut dosyası işlevine kapsamlamak için bir yol tanımlamaz , bir dosyadan çok daha azdır.
Bu, tüm değişkenleri etkili bir şekilde küresel hale getirir , bu da tekrar modülerlik ve kompozisyona zarar verir.
Kesinlikle içinde - Orada çözümler sonrası POSIX kabuklarda bu üzeresiniz bash
, ksh93
ve zsh
en azından - ama bu sadece geri Yukarıda verilen 1 götürür.
Bunun stil kılavuzlarındaki etkisini GNU Autoconf makro yazısında görebilirsiniz, burada değişken adlarını makronun adıyla önek olarak eklemenizi öneririz, bu da çarpışmanın kabul edilebilir şekilde yakınına düşme olasılığını azaltmak için çok uzun değişken isimlerine yol açar sıfır.
C bile bu skorda bir mil daha iyidir. Çoğu C programı öncelikli olarak fonksiyon-yerel değişkenlerle yazılmakla kalmaz, C aynı zamanda blok kapsamayı destekler ve tek bir fonksiyon içindeki birden fazla bloğun değişken isimlerini çapraz kontaminasyon olmadan yeniden kullanmasına izin verir.
Shell programlama dillerinde standart kitaplık yoktur.
Bir kabuk betik dilinin standart kütüphanesinin içeriği olduğunu iddia etmek mümkündür PATH
, ancak bu sadece sonuç almak için bir kabuk betiğinin başka bir programa çağırması gerektiğini, muhtemelen daha güçlü bir dilde yazılmış olduğunu söylemektedir . ile başlar.
Perl CPAN'da olduğu gibi, yaygın olarak kullanılan kabuk yardımcı programı kütüphaneleri arşivi de yoktur . Geniş bir üçüncü taraf yardımcı program kodu kütüphanesi olmadan, bir programcı elle daha fazla kod yazmalıdır, bu yüzden daha az üretken olur.
Hatta en kabuk komut bitti yararlı bir şey almak için genellikle C yazılır dış programlar itimat gerçeğini göz ardı ederek herkesin havai var pipe()
→ fork()
→ exec()
çağrı zincirleri. Bu desen, Unix'te IPC ve diğer işletim sistemlerinde işlem başlatma ile karşılaştırıldığında oldukça etkilidir , ancak burada yapacağınız işi , başka bir komut dosyası dilinde alt program çağrısıyla etkili bir şekilde değiştirir , bu da çok daha verimlidir. Bu, kabuk komut dosyası yürütme hızının üst sınırına ciddi bir sınır koyar.
Kabuk betiklerinin paralel yürütme yoluyla performanslarını arttırma özelliği çok azdır.
Bourne mermileri ve bunun için boru hatları var &
, wait
ancak bu büyük ölçüde sadece birden fazla program oluşturmak için yararlıdır, CPU veya G / Ç paralelliğine ulaşmak için değil. Büyük olasılıkla muktedir değiliz peg çekirdek veya kabuk komut dosyası ile yalnızca RAID düzenini doyurmak ve bunu yaparsanız, muhtemelen diğer dillerde çok daha yüksek bir performans elde edebiliriz.
Özellikle boru hatları, paralel yürütme yoluyla performansı artırmanın zayıf yoludur. Yalnızca iki programın paralel olarak çalışmasına izin verir ve ikisinden biri belirli bir zamanda G / Ç'de veya diğerinden diğerine engellenir .
Bunun etrafında xargs -P
ve GNUparallel
gibi son gün yolları vardır , ancak bu sadece yukarıda 4. maddeye dönüşür.
Çok işlemcili sistemlerden tam olarak yararlanabilmek için yerleşik bir yeteneği olmadığında, kabuk komut dosyaları, sistemdeki tüm işlemcileri kullanabilen bir dilde iyi yazılmış bir programdan her zaman daha yavaş olacaktır. Bu GNU Autoconf configure
komut dosyası örneğini tekrar almak için sistemdeki çekirdek sayısını iki katına çıkarmak, çalışma hızını artırmak için çok az şey yapar.
Kabuk kodlama dillerinde işaretçiler veya referanslar yoktur .
Bu, diğer programlama dillerinde kolayca bir sürü şey yapmanızı engeller.
Birincisi, programın belleğindeki başka bir veri yapısına dolaylı olarak atıfta bulunmamak, yerleşik veri yapılarıyla sınırlı olduğunuz anlamına gelir . Kabuğunuzda ilişkilendirilebilir diziler olabilir , ancak bunlar nasıl uygulanır? Her biri farklı dengesizliklere sahip birkaç olasılık vardır: kırmızı-siyah ağaçlar , AVL ağaçları ve hash tabloları en yaygın olanlarıdır, ancak diğerleri vardır. Farklı bir takas dizisine ihtiyacınız varsa, sıkışmışsınızdır, çünkü referanslar olmadan, birçok gelişmiş veri yapısını elle yuvarlamanın bir yolu yoktur. Size verilenlerle takıldınız.
Veya, bir bağımlılık grafiğini modellemek için ihtiyaç duyabileceğiniz, yönlendirilmiş bir döngüsel grafik gibi kabuk komut dosyası yorumcunuza yerleşik yeterli bir alternatifi olmayan bir veri yapısına ihtiyacınız olabilir . Onlarca yıldır programlama yapıyorum ve bunu bir kabuk komut dosyasında yapmayı düşünebilmemin tek yolu dosya sistemini kötüye kullanmak, sahte bağlantıları sahte semboller olarak kullanmak olacaktır. Bu sadece Turing-bütünlüğüne güvendiğinizde elde ettiğiniz bir çözümdür, bu da size çözümün zarif, hızlı veya anlaşılması kolay olup olmadığı hakkında hiçbir şey söylemez.
Gelişmiş veri yapıları yalnızca işaretçiler ve referanslar için bir kullanımdır. Onlar için Bourne ailesi kabuk komut dosyası dilinde kolayca yapılamayan diğer uygulama yığınları vardır .
Devam edebilirdim, ama bence bu noktaya değiniyorsun. Basitçe söylemek gerekirse, Unix tipi sistemler için çok daha güçlü programlama dilleri vardır.
Bu, bazı durumlarda dilin sıradanlığını telafi edebilecek büyük bir avantajdır.
Elbette, bu yüzden GNU Autoconf, configure
komut dosyası çıktıları için Bourne kabuk komut dosyası dil ailesinin bilerek kısıtlanmış bir alt kümesini kullanır : böylece configure
komut dosyaları hemen hemen her yerde çalışır.
Muhtemelen GNU Autoconf'un geliştiricilerinden daha taşınabilir bir Bourne kabuğu lehçesinde yazma yararına daha büyük bir inanan grubu bulamayacaksınız, ancak kendi yaratımları öncelikle Perl'de, artı bazılarında m4
ve sadece biraz kabukta yazılıyor senaryo; sadece Autoconf'un çıktısı saf bir Bourne kabuk betiğidir. Bu, "Her yerde Bourne" kavramının ne kadar yararlı olduğu sorusuna yol açmazsa, ne olacağını bilmiyorum.
Peki, bu tür programların ne kadar karmaşık olabileceğinin bir sınırı var mı?
Teknik olarak konuşursak, hayır, Turing-bütünlük gözleminizin de belirttiği gibi.
Ancak bu, keyfi olarak büyük kabuk komut dosyalarının yazmak için hoş, hata ayıklaması kolay veya hızlı çalıştırılması demek değildir.
Saf bash'da bir dosya kompresörü / dekompresörü yazmak mümkün müdür?
"Saf" Bash, herhangi bir şey söylemeden PATH
? Kompresör muhtemelen echo
onaltılık kaçış dizileri kullanılarak yapılabilir , ancak yapılması oldukça acı verici olur. Dekompresör, kabuktaki ikili verilerin işlenememesi nedeniyle bu şekilde yazmak imkansız olabilir . Sonunda od
, kabuğun yerel veri işleme biçimi olan ikili verileri metin biçimine çevirmek için arama yaparsınız .
Kabuk komut dosyasını amaçlandığı şekilde kullanmaya başladığınızda, diğer programları sürmek için tutkal olarak PATH
, kapılar açılır, çünkü şimdi sadece diğer programlama dillerinde yapılabileceklerle sınırlısınız, yani hiç sınırı yok. Tüm gücünü diğer programlara çağırarak bir kabuk betiği PATH
daha güçlü dillerde yazılmış monolitik programlar kadar hızlı çalışmaz , ancak çalışır.
İşte mesele bu. Hızlı çalıştırmak için bir programa ihtiyacınız varsa veya başkalarından güç almak yerine kendi başına güçlü olması gerekiyorsa, onu kabukta yazmazsınız.
Basit bir video oyunu mu?
İşte Kabuktaki Tetris . Eğer aramaya giderseniz, bu tür diğer oyunlar da mevcuttur.
sadece çok sınırlı hata ayıklama araçları var
Büyük programlamayı desteklemek için gerekli özellikler listesinde hata ayıklama aracı desteğini yaklaşık 20. sıraya koyardım. Birçok programcı , dilden bağımsız olarak, uygun hata ayıklayıcılardan çok daha fazla printf()
hata ayıklamaya güvenir .
Kabukta, echo
ve set -x
çok sayıda sorunu ayıklamak için birlikte olan yeterlidir.
sh
Senaryoconfigure
pek çok un * x paketleri oluşturma sürecinin bir parçası olarak kullanılmaktadır 'nispeten basit' değil.