Kabuk, işletim sistemi için bir arayüzdür. Genellikle kendi başına aşağı yukarı sağlam bir programlama dilidir, ancak özellikle işletim sistemi ve dosya sistemi ile etkileşimi kolaylaştırmak için tasarlanmış özelliklere sahiptir. POSIX kabuğunun (bundan sonra sadece "kabuk" olarak anılacaktır) semantiği, LISP'nin (s-ifadelerinin kabuk sözcük bölme ile pek çok ortak noktası vardır ) ve C'nin (kabuğun aritmetik sözdiziminin çoğu) bazı özelliklerini birleştiren bir mutt'tur. anlambilim C'den gelir).
Kabuğun sözdiziminin diğer kökü, tek tek UNIX yardımcı programlarının bir karışıklığı olarak yetiştirilmesinden gelir. Kabukta genellikle yerleşik olanların çoğu, aslında harici komutlar olarak uygulanabilir. /bin/[
Birçok sistemde var olduğunu fark ettiklerinde, bir döngü için birçok kabuk neofitini fırlatır .
$ if '/bin/[' -f '/bin/['; then echo t; fi
t
wat?
Kabuğun nasıl uygulandığına bakarsanız, bu çok daha mantıklıdır. İşte alıştırma olarak yaptığım bir uygulama. Python'da, ama umarım bu kimse için bir sorun değildir. Çok sağlam değil ama öğretici:
#!/usr/bin/env python
from __future__ import print_function
import os, sys
'''Hacky barebones shell.'''
try:
input=raw_input
except NameError:
pass
def main():
while True:
cmd = input('prompt> ')
args = cmd.split()
if not args:
continue
cpid = os.fork()
if cpid == 0:
os.execl(args[0], *args)
else:
os.waitpid(cpid, 0)
if __name__ == '__main__':
main()
Umarım yukarıdakiler, bir kabuğun yürütme modelinin oldukça fazla olduğunu açıkça ortaya koyar:
1. Expand words.
2. Assume the first word is a command.
3. Execute that command with the following words as arguments.
Genişletme, komut çözümleme, yürütme. Kabuğun tüm semantiği, yukarıda yazdığım uygulamadan çok daha zengin olsalar da, bu üç şeyden birine bağlıdır.
Tüm komutlar değil fork
. Aslında, harici olarak uygulanmış bir ton anlam ifade etmeyen (böyle yapmak zorunda kalacakları gibi) bir avuç komut vardır fork
, ancak bunlar bile katı POSIX uyumluluğu için genellikle harici olarak mevcuttur.
Bash, POSIX kabuğunu geliştirmek için yeni özellikler ve anahtar sözcükler ekleyerek bu temele dayanmaktadır. Neredeyse sh ile uyumludur ve bash o kadar yaygındır ki, bazı komut dosyası yazarları yıllarca bir betiğin aslında POSIX açısından katı bir sistemde çalışmayabileceğini fark etmeden geçirirler. (İnsanların bir programlama dilinin anlambilimini ve stilini nasıl bu kadar çok önemsediğini ve kabuğun anlambilim ve stilini çok az önemsediğini de merak ediyorum, ama ben ayrılıyorum.)
Değerlendirme sırası
Bu biraz hileli bir sorudur: Bash, ifadeleri soldan sağa birincil sözdiziminde yorumlar, ancak aritmetik sözdiziminde C önceliğini izler. Yine de ifadeler genişletmelerden farklıdır . Gönderen EXPANSION
bash kılavuzun bölümünde:
Genişletme sırası şöyledir: küme genişletme; yaklaşık genişletme, parametre ve değişken genişletme, aritmetik genişletme ve komut ikamesi (soldan sağa bir şekilde yapılır); kelime bölme; ve yol adı genişletmesi.
Sözcük bölme, yol adı genişletme ve parametre genişletmeyi anlıyorsanız, bash'ın ne yaptığını anlama yolunda ilerliyorsunuz demektir. Sözcük ayırmadan sonra gelen yol adı genişletmesinin kritik olduğunu unutmayın, çünkü adında boşluk olan bir dosyanın bir glob ile eşleştirilebilmesini sağlar. Bu nedenle, glob genişletmelerinin iyi kullanılması genel olarak komutları ayrıştırmaktan daha iyidir .
Dürbün
İşlev kapsamı
Eski ECMAscript'e çok benzer şekilde, bir işlev içinde adları açıkça belirtmediğiniz sürece kabuk dinamik kapsama sahiptir.
$ foo() { echo $x; }
$ bar() { local x; echo $x; }
$ foo
$ bar
$ x=123
$ foo
123
$ bar
$ …
Çevre ve süreç "kapsamı"
Alt kabuklar, üst kabuklarının değişkenlerini devralır, ancak diğer türden işlemler aktarılmayan adları miras almaz.
$ x=123
$ ( echo $x )
123
$ bash -c 'echo $x'
$ export x
$ bash -c 'echo $x'
123
$ y=123 bash -c 'echo $y'
123
Bu kapsam belirleme kurallarını birleştirebilirsiniz:
$ foo() {
> local -x bar=123
> bash -c 'echo $bar'
> }
$ foo
123
$ echo $bar
$
Yazma disiplini
Tipler. Evet. Bash'in gerçekten türleri yoktur ve her şey bir dizeye genişler (veya belki bir kelime daha uygun olur). Ancak farklı türdeki genişletmeleri inceleyelim.
Teller
Hemen hemen her şey bir dizge olarak değerlendirilebilir. Bash'deki barewords, anlamı tamamen kendisine uygulanan genişlemeye bağlı olan dizelerdir.
Genişleme yok
Çıplak bir kelimenin gerçekten sadece bir kelime olduğunu ve bu alıntıların bu konuda hiçbir şeyi değiştirmediğini göstermek faydalı olabilir.
$ echo foo
foo
$ 'echo' foo
foo
$ "echo" foo
foo
Alt dize genişletme
$ fail='echoes'
$ set -x
$ "${fail:0:-2}" Hello World
+ echo Hello World
Hello World
Genişletmelerle ilgili daha fazla bilgi Parameter Expansion
için kılavuzun bölümünü okuyun . Oldukça güçlü.
Tamsayılar ve aritmetik ifadeler
Kabuğa atama ifadelerinin sağ tarafını aritmetik olarak ele almasını söylemek için tamsayı özniteliğiyle isimleri aşılayabilirsiniz. Daha sonra, parametre genişlediğinde,… bir dizeye genişletilmeden önce tamsayı matematik olarak değerlendirilecektir.
$ foo=10+10
$ echo $foo
10+10
$ declare -i foo
$ foo=$foo
$ echo $foo
20
$ echo "${foo:0:1}"
2
Diziler
Bağımsız Değişkenler ve Konumsal Parametreler
Diziler hakkında konuşmadan önce konumsal parametreleri tartışmaya değer olabilir. Bir kabuk komut argümanlar numaralı parametreler aracılığıyla ulaşılabilir $1
, $2
, $3
vb kullanarak bir kerede tüm bu parametreleri erişebilir "$@"
dizilerle ortak noktası çok şey vardır ki genişleme. Konumsal parametreleri, set
veya shift
yerleşiklerini kullanarak veya sadece şu parametrelerle kabuğu veya bir kabuk işlevini çağırarak ayarlayabilir ve değiştirebilirsiniz :
$ bash -c 'for ((i=1;i<=$#;i++)); do
> printf "\$%d => %s\n" "$i" "${@:i:1}"
> done' -- foo bar baz
$1 => foo
$2 => bar
$3 => baz
$ showpp() {
> local i
> for ((i=1;i<=$#;i++)); do
> printf '$%d => %s\n' "$i" "${@:i:1}"
> done
> }
$ showpp foo bar baz
$1 => foo
$2 => bar
$3 => baz
$ showshift() {
> shift 3
> showpp "$@"
> }
$ showshift foo bar baz biz quux xyzzy
$1 => biz
$2 => quux
$3 => xyzzy
Bash el kitabına bazen $0
konumsal bir parametre olarak da başvurulur . Bunu kafa karıştırıcı buluyorum, çünkü bunu argüman sayısına dahil etmiyor $#
, ancak numaralı bir parametre, yani meh. $0
kabuğun veya mevcut kabuk betiğinin adıdır.
Diziler
Dizilerin sözdizimi konumsal parametrelerden sonra modellenir, bu nedenle dizileri adlandırılmış bir tür "harici konumsal parametreler" olarak düşünmek çoğunlukla sağlıklıdır. Diziler, aşağıdaki yaklaşımlar kullanılarak bildirilebilir:
$ foo=( element0 element1 element2 )
$ bar[3]=element3
$ baz=( [12]=element12 [0]=element0 )
Dizi öğelerine dizine göre erişebilirsiniz:
$ echo "${foo[1]}"
element1
Dizileri dilimleyebilirsiniz:
$ printf '"%s"\n' "${foo[@]:1}"
"element1"
"element2"
Bir diziyi normal bir parametre olarak kabul ederseniz, sıfırıncı indeksi elde edersiniz.
$ echo "$baz"
element0
$ echo "$bar"
$ …
Sözcük bölünmesini önlemek için tırnak işaretleri veya ters eğik çizgiler kullanırsanız, dizi belirtilen sözcük bölmesini korur:
$ foo=( 'elementa b c' 'd e f' )
$ echo "${#foo[@]}"
2
Diziler ve konumsal parametreler arasındaki temel fark:
- Konumsal parametreler seyrek değildir. Eğer
$12
ayarlanır, emin olabilirsiniz $11
de ayarlanır. (Boş dizeye ayarlanabilir, ancak $#
12'den küçük olmayacaktır.) Ayarlanırsa "${arr[12]}"
, ayarlanmış bir garanti yoktur "${arr[11]}"
ve dizinin uzunluğu 1 kadar küçük olabilir.
- Bir dizinin sıfırıncı öğesi, o dizinin açık bir şekilde sıfırıncı öğesidir. Konumsal parametrelerde, sıfırıncı öğe ilk argüman değil, kabuğun veya kabuk betiğinin adıdır.
- To
shift
dizisi, sen dilime sahip ve benzeri atanması arr=( "${arr[@]:1}" )
. Bunu da yapabilirsiniz unset arr[0]
, ancak bu 1. indeksteki ilk öğeyi oluşturur.
- Diziler, küresel olarak kabuk işlevleri arasında örtük olarak paylaşılabilir, ancak bunları görebilmesi için konumsal parametreleri açıkça bir kabuk işlevine iletmeniz gerekir.
Dosya adı dizilerini oluşturmak için yol adı genişletmelerini kullanmak genellikle uygundur:
$ dirs=( */ )
Komutlar
Komutlar anahtardır, ancak aynı zamanda kılavuzda yapabildiğimden daha derinlemesine ele alınmıştır. SHELL GRAMMAR
Bölümü okuyun . Farklı türdeki komutlar şunlardır:
- Basit Komutlar (örneğin
$ startx
)
- Boru hatları (ör.
$ yes | make config
) (Lol)
- Listeler (örneğin
$ grep -qF foo file && sed 's/foo/bar/' file > newfile
)
- Bileşik Komutlar (ör.
$ ( cd -P /var/www/webroot && echo "webroot is $PWD" )
)
- Eş-süreçler (Karmaşık, örnek yok)
- Fonksiyonlar (Basit bir komut olarak değerlendirilebilen adlandırılmış bir bileşik komut)
Yürütme Modeli
Uygulama modeli elbette hem bir yığın hem de bir yığın içerir. Bu, tüm UNIX programları için endemiktir. Bash ayrıca, caller
yerleşiklerin iç içe kullanımıyla görülebilen kabuk işlevleri için bir çağrı yığınına sahiptir .
Referanslar:
SHELL GRAMMAR
Darbe kılavuzun bölümü
- XCU Shell Komut Dili dokümantasyon
- Bash Kılavuzu Greycat wiki üzerinde.
- UNIX Ortamında Gelişmiş Programlama
Belirli bir yönde daha da genişletmemi istiyorsanız lütfen yorum yapın.