Bash betikleri için anlambilim mi?


88

Bildiğim diğer dillerden daha fazla, her küçük şeye ihtiyacım olduğunda Google'da Bash'i "öğrendim". Sonuç olarak, işe yarıyor gibi görünen küçük komut dosyalarına yama uygulayabilirim. Ancak, neler olup bittiğini gerçekten bilmiyorum ve bir programlama dili olarak Bash'e daha resmi bir giriş yapmayı umuyordum. Örneğin: Değerlendirme sırası nedir? kapsam kuralları nelerdir? Yazma disiplini nedir, örneğin her şey bir dizge mi? Programın durumu nedir - dizelerin değişken adlarına anahtar-değer ataması mıdır; bundan daha fazlası var mı, örneğin yığın? Bir yığın var mı? Ve bunun gibi.

Bu tür bir içgörü için GNU Bash kılavuzuna başvurmayı düşündüm, ama istediğim bu değil gibi görünüyor; temel anlamsal modelin bir açıklamasından çok, sözdizimsel şekerden oluşan bir çamaşır listesi gibidir. Çevrimiçi milyonlarca "bash öğreticisi" yalnızca daha kötü. Belki de önce ders çalışmalı shve Bash'i bunun üzerine sözdizimsel bir şeker olarak anlamalıyım? Yine de bunun doğru bir model olup olmadığını bilmiyorum.

Herhangi bir öneri?

DÜZENLEME: İdeal olarak ne aradığıma dair örnekler vermem istendi. "Biçimsel anlambilim" olarak kabul edeceğim şeyin oldukça uç bir örneği , "JavaScript'in özü" hakkındaki bu makale . Belki biraz daha az resmi bir örnek Haskell 2010 raporudur .


3
Gelişmiş Bash Scripting Guide "milyonda ve bir" biri?
choroba

2
Bash'in "çekirdek anlamsal bir modele" sahip olduğuna ikna olmadım (belki "hemen hemen her şey bir dizedir"); bence gerçekten sözdizimsel bir şeker.
Gordon Davisson

4
"Sözdizimsel şekerin çamaşır listesi" dediğiniz şey aslında anlamsal genişleme modelidir - uygulamanın son derece önemli bir parçasıdır. Hataların ve kafa karışıklığının% 90'ı, genişleme modelini anlamamaktan kaynaklanıyor.
diğer adam

4
Birisi gibi okursanız bu geniş bir soru olduğunu düşünebilir neden görebilirsiniz ben bir kabuk yazılımı nasıl ? Ancak asıl soru, özellikle kabuk dili ve bash için biçimsel anlam ve temel nedir? ve tutarlı tek bir cevabı olan iyi bir sorudur. Yeniden açılması için oylama.
kojiro

1
Linuxcommand.org'da epey bir şey öğrendim ve hatta komut satırında ve kabuk betikleri yazarken daha derinlemesine bir kitabın ücretsiz bir pdf'si bile var
samrap

Yanıtlar:


107

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 # Tested as-is on OS X, without the `]`
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:
      # We're in a child process
      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 EXPANSIONbash 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' # another way to transiently export a name
123

Bu kapsam belirleme kurallarını birleştirebilirsiniz:

$ foo() {
>   local -x bar=123 # Export foo, but only in this scope
>   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 # So we can see what's going on
$ "${fail:0:-2}" Hello World
+ echo Hello World
Hello World

Genişletmelerle ilgili daha fazla bilgi Parameter Expansioniç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 # Must re-evaluate the assignment
$ echo $foo
20
$ echo "${foo:0:1}" # Still just a string
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, $3vb kullanarak bir kerede tüm bu parametreleri erişebilir "$@"dizilerle ortak noktası çok şey vardır ki genişleme. Konumsal parametreleri, setveya shiftyerleş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 $0konumsal 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. $0kabuğ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" # Even if the zeroth index isn't set

$ …

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:

  1. Konumsal parametreler seyrek değildir. Eğer $12ayarlanır, emin olabilirsiniz $11de 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.
  2. 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.
  3. To shiftdizisi, sen dilime sahip ve benzeri atanması arr=( "${arr[@]:1}" ). Bunu da yapabilirsiniz unset arr[0], ancak bu 1. indeksteki ilk öğeyi oluşturur.
  4. 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 GRAMMARBölümü okuyun . Farklı türdeki komutlar şunlardır:

  1. Basit Komutlar (örneğin $ startx)
  2. Boru hatları (ör. $ yes | make config) (Lol)
  3. Listeler (örneğin $ grep -qF foo file && sed 's/foo/bar/' file > newfile)
  4. Bileşik Komutlar (ör. $ ( cd -P /var/www/webroot && echo "webroot is $PWD" ))
  5. Eş-süreçler (Karmaşık, örnek yok)
  6. 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, calleryerleşiklerin iç içe kullanımıyla görülebilen kabuk işlevleri için bir çağrı yığınına sahiptir .

Referanslar:

  1. SHELL GRAMMARDarbe kılavuzun bölümü
  2. XCU Shell Komut Dili dokümantasyon
  3. Bash Kılavuzu Greycat wiki üzerinde.
  4. UNIX Ortamında Gelişmiş Programlama

Belirli bir yönde daha da genişletmemi istiyorsanız lütfen yorum yapın.


16
+1: Harika bir açıklama. Örneklerle bunu yazmak için harcadığınız zamanı takdir edin.
jaypal singh

yes | make config;-) için +1 Ama cidden, çok iyi bir yazı.
Dijital Travma

bunu okumaya yeni başladım .. güzel. bazı yorumlar bırakacak. 1) Bunu gördüğünüzde daha da büyük bir sürpriz gelir /bin/[ve /bin/testgenellikle aynı uygulama 2) "İlk kelimenin bir komut olduğunu varsayın." - atama yaptığınızda bekleyin ...
Karoly Horvath

@KarolyHorvath Evet, atamayı kasıtlı olarak demo kabuğumdan hariç tuttum çünkü değişkenler karmaşık bir karmaşa. Bu demo kabuğu, bu cevap akılda tutularak yazılmadı - çok daha önce yazılmıştı. Sanırım bunu yapabilir execleve ilk kelimeleri ortama ekleyebilirim, ama bu yine de onu biraz daha karmaşık hale getirir.
kojiro

@kojiro: nah bu onu aşırı karmaşık hale getirir, kesinlikle niyetim bu değildi! ancak atama biraz farklı çalışır (x) ve IMHO bunu metinde bir yerde belirtmelisiniz. (x): ve biraz kafa karışıklığının kaynağı ... Artık çalışmadığından şikayet eden insanları kaç kez gördüğümü a = 1bile sayamıyorum).
Karoly Horvath

5

"Yazma disiplini nedir, örneğin her şey bir dizedir" sorunuzun cevabı Bash değişkenleri karakter dizeleridir. Ancak, Bash, değişkenler tam sayı olduğunda aritmetik işlemlere ve değişkenler üzerinde karşılaştırmalara izin verir. Bash değişkenleri kuralının istisnası, karakter dizeleridir, söz konusu değişkenlerin dizildiği veya başka türlü bildirildiği zamandır

$ A=10/2
$ echo "A = $A"           # Variable A acting like a String.
A = 10/2

$ B=1
$ let B="$B+1"            # Let is internal to bash.
$ echo "B = $B"           # One is added to B was Behaving as an integer.
B = 2

$ A=1024                  # A Defaults to string
$ B=${A/24/STRING01}      # Substitute "24"  with "STRING01".
$ echo "B = $B"           # $B STRING is a string
B = 10STRING01

$ B=${A/24/STRING01}      # Substitute "24"  with "STRING01".
$ declare -i B
$ echo "B = $B"           # Declaring a variable with non-integers in it doesn't change the contents.
B = 10STRING01

$ B=${B/STRING01/24}      # Substitute "STRING01"  with "24".
$ echo "B = $B"
B = 1024

$ declare -i B=10/2       # Declare B and assigning it an integer value
$ echo "B = $B"           # Variable B behaving as an Integer
B = 5

Seçenek anlamlarını beyan edin:

  • -a Değişken bir dizidir.
  • -f Yalnızca işlev adlarını kullanın.
  • -i Değişken, bir tamsayı olarak ele alınmalıdır; aritmetik değerlendirme, değişkene bir değer atandığında gerçekleştirilir.
  • -p Her değişkenin özniteliklerini ve değerlerini görüntüleyin. -P kullanıldığında, ek seçenekler göz ardı edilir.
  • -r Değişkenleri salt okunur yapın. Bu değişkenler daha sonra sonraki atama ifadeleri tarafından atanamaz veya ayarlanamazlar.
  • -t Her değişkene izleme özniteliğini verin.
  • -x Ortam aracılığıyla sonraki komutlara aktarım için her değişkeni işaretleyin.

1

Bash man sayfası, çoğu yönetim sayfasından biraz daha fazla bilgiye sahiptir ve istediğiniz şeylerden bazılarını içerir. On yıldan fazla bir komut dosyası bashından sonra benim varsayım, 'sh'nin bir uzantısı olarak geçmişinden dolayı, bazı garip sözdizimlerine sahip olduğudur (sh ile geriye dönük uyumluluğu korumak için).

FWIW, benim deneyimim seninki gibi oldu; çeşitli kitaplar (örneğin, O'Reilly "Bash Kabuğunu Öğrenmek" ve benzeri) sözdizimine yardımcı olmasına rağmen, çeşitli sorunları çözmenin birçok garip yolu vardır ve bunlardan bazıları kitapta yer almamaktadır ve Google'da aranmalıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.