Semantyka dla skryptów Bash?

Bardziej niż jakikolwiek inny język, który znam, "nauczyłem się" Basha przez Googlowanie za każdym razem, gdy potrzebuję jakiegoś drobiazgu. W związku z tym, mogę patchwork razem małe skrypty, które wydają się działać. Jednak nie wiem naprawdę o co chodzi i liczyłem na bardziej formalne wprowadzenie do Basha jako języka programowania. Na przykład: co to jest kolejność oceny? jakie są zasady ustalania zakresu? Czym jest dyscyplina typowania, np. czy wszystko jest ciągiem znaków? Jaki jest stan programu-jest jest to przypisanie łańcuchów do nazw zmiennych; czy jest ich więcej, np. stos? Jest tam sterta? I tak dalej.

Pomyślałem, że zajrzę do podręcznika GNU Bash w celu uzyskania tego rodzaju wglądu, ale nie wydaje mi się, aby to było to, czego chcę; jest to bardziej lista cukru składniowego niż Wyjaśnienie podstawowego modelu semantycznego. Milion i jeden "tutoriale bash" online są tylko gorsze. Może powinienem najpierw przestudiować sh i zrozumieć Basha jako cukier składniowy? I Nie wiem, czy jest to dokładny model.

Jakieś sugestie?

EDIT: poproszono mnie o podanie przykładów tego, czego szukam. Dość ekstremalnym przykładem tego, co uznałbym za "semantykę formalną", jest ten artykuł o "istocie JavaScript" . Być może nieco mniej formalnym przykładem jest raport Haskell 2010 .

Author: jameshfisher, 2014-04-22

3 answers

Powłoka jest interfejsem dla systemu operacyjnego. Jest to zwykle bardziej lub mniej solidny język programowania sam w sobie, ale z funkcjami zaprojektowanymi tak, aby ułatwić interakcję z systemem operacyjnym i systemem plików. Semantyka powłoki POSIX (dalej określana jako "powłoka") jest nieco muttem, łączącym pewne cechy Lispu (wyrażenia s mają wiele wspólnego z shellem dzielenie wyrazów ) i C (większość arytmetyki powłoki składnia semantyka pochodzi z języka C).

Drugi rdzeń składni powłoki pochodzi z jej wychowania jako miszmasz poszczególnych narzędzi uniksowych. Większość tego, co często jest wbudowane w powłokę, może być zaimplementowana jako zewnętrzne polecenia. Rzuca wiele neofitów powłoki dla pętli, gdy zdają sobie sprawę, że {16]} istnieje w wielu systemach.

$ if '/bin/[' -f '/bin/['; then echo t; fi # Tested as-is on OS X, without the `]`
t
Wat?

To ma o wiele większy sens, jeśli przyjrzymy się, jak powłoka jest zaimplementowana. Oto implementacja, którą zrobiłem jako ćwiczenia. Jest w Pythonie, ale mam nadzieję, że to nie jest rozłąka dla nikogo. Nie jest strasznie wytrzymały, ale jest pouczający:

#!/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()

Mam nadzieję, że powyższe wyjaśnia, że model wykonania powłoki jest w zasadzie:

1. Expand words.
2. Assume the first word is a command.
3. Execute that command with the following words as arguments.

Rozszerzenie, rozwiązywanie poleceń, wykonywanie. Cała semantyka powłoki jest związana z jedną z tych trzech rzeczy, chociaż są one znacznie bogatsze niż implementacja, którą napisałem powyżej.

Nie wszystkie komendy fork. W rzeczywistości jest kilka polecenia, które nie sprawiają, że ma mnóstwo sensu zaimplementowane jako zewnętrzne (tak, że musiałyby fork), ale nawet te są często dostępne jako zewnętrzne dla ścisłej zgodności z POSIX.

Bash opiera się na tej podstawie, dodając nowe funkcje i słowa kluczowe w celu ulepszenia powłoki POSIX. Jest prawie kompatybilny z sh, a bash jest tak wszechobecny, że niektórzy autorzy skryptów przechodzą lata, nie zdając sobie sprawy, że skrypt może nie działać na ściśle określonym systemie. (Zastanawiam się też, jak ludzie może troszczyć się tak bardzo o semantykę i styl jednego języka programowania, a tak mało o semantykę i styl powłoki, ale różnię się.)

Kolejność oceny

Jest to trochę podchwytliwe pytanie: Bash interpretuje wyrażenia w swojej podstawowej składni od lewej do prawej, ale w swojej składni arytmetycznej stosuje pierwszeństwo C. Wyrażenia różnią się jednak od rozszerzeń . Z sekcji EXPANSION podręcznika bash:

The order of expansions is: rozszerzenie klamry; rozszerzenie tyldy, parametr i rozszerzanie zmiennych, Rozszerzanie arytmetyczne i zastępowanie poleceń (zrobione w sposób od lewej do prawej); podział słów; i rozszerzenie ścieżki.

Jeśli rozumiesz wordsplitting, rozszerzenie pathname i rozszerzenie parametrów, jesteś na dobrej drodze do zrozumienia większości tego, co robi bash. Zauważ, że rozszerzenie pathname pojawiające się po wordsplitting jest krytyczne, ponieważ zapewnia, że plik z białymi spacjami w nazwie może / align = "left" / Dlatego dobre wykorzystanie rozszerzeń glob jest lepsze niżparsowanie poleceń , ogólnie rzecz biorąc.

Zakres

Zakres funkcji

Podobnie jak stary ECMAscript, powłoka ma dynamiczny zakres, chyba że jawnie zadeklarujesz nazwy w funkcji.

$ foo() { echo $x; }
$ bar() { local x; echo $x; }
$ foo

$ bar

$ x=123
$ foo
123
$ bar

$ …

Środowisko i proces "zakres"

Podzbiory dziedziczą zmienne swoich powłok nadrzędnych, ale inne rodzaje procesów nie dziedziczą nieprzetworzonych nazwiska.

$ 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

Możesz połączyć te zasady:

$ foo() {
>   local -x bar=123 # Export foo, but only in this scope
>   bash -c 'echo $bar'
> }
$ foo
123
$ echo $bar

$

Typowanie dyscypliny

Typy. Tak. Bash naprawdę nie ma typów, a wszystko rozszerza się do ciągu znaków (lub być może słowo byłoby bardziej odpowiednie.) Ale przyjrzyjmy się różnym rodzajom rozszerzeń.

Struny

Prawie wszystko można traktować jako ciąg znaków. Barewords w bash to ciągi znaków, których znaczenie zależy wyłącznie od zastosowanego rozszerzenia.

Nie. rozbudowa

Może warto pokazać, że nagie słowo to naprawdę tylko słowo, a cytaty nic w tym nie zmieniają.

$ echo foo
foo
$ 'echo' foo
foo
$ "echo" foo
foo
Ekspansja podciągu
$ fail='echoes'
$ set -x # So we can see what's going on
$ "${fail:0:-2}" Hello World
+ echo Hello World
Hello World

Aby dowiedzieć się więcej o rozszerzeniach, przeczytaj sekcję Parameter Expansion podręcznika. Jest dość potężny.

Liczby całkowite i wyrażenia arytmetyczne

Możesz nadać nazwom atrybut integer, aby powiedzieć powłoce, aby traktowała prawą stronę wyrażeń przypisania jako arytmetykę. Następnie, gdy parametr rozszerza się zostanie ona obliczona jako liczba całkowita przed rozszerzeniem do ... ciągu znaków.

$ 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

Tablice

Argumenty i parametry pozycyjne

Przed rozmową o tablicach warto przedyskutować parametry pozycyjne. Argumenty skryptu powłoki mogą być dostępne za pomocą numerowanych parametrów, $1, $2, $3, itd. Możesz uzyskać dostęp do wszystkich tych parametrów na raz za pomocą "$@", które rozszerzenie ma wiele wspólnego z tablicami. Możesz ustawić I zmienić parametry pozycyjne za pomocą w przeciwieństwie do innych funkcji, powłoka może być użyta do tworzenia powłoki.]}

$ 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

Podręcznik bash czasami odnosi się do $0 jako parametru pozycyjnego. Uważam to za mylące, ponieważ nie zawiera go w argumencie count $#, ale jest to parametr numerowany, więc meh. $0 jest nazwą powłoki lub bieżącego skryptu powłoki.

Tablice

Składnia tablic jest wzorowana na parametrach pozycyjnych, więc zdrowo jest myśleć tablic jako nazwanego rodzaju "zewnętrznych parametrów pozycyjnych", jeśli chcesz. Tablice Można zadeklarować przy użyciu następujących metod:

$ foo=( element0 element1 element2 )
$ bar[3]=element3
$ baz=( [12]=element12 [0]=element0 )

Możesz uzyskać dostęp do elementów tablicy według indeksu:

$ echo "${foo[1]}"
element1

Możesz pokroić tablice:

$ printf '"%s"\n' "${foo[@]:1}"
"element1"
"element2"

Jeśli potraktujesz tablicę jako normalny parametr, otrzymasz indeks zerowy.

$ echo "$baz"
element0
$ echo "$bar" # Even if the zeroth index isn't set

$ …

Jeśli używasz cudzysłowów lub odwrotnych ukośników, aby zapobiec rozdzielaniu słów, tablica zachowa określone rozdzielanie słów:

$ foo=( 'elementa b c' 'd e f' )
$ echo "${#foo[@]}"
2

Główny różnica między tablicami a parametrami pozycyjnymi wynosi:

  1. parametry pozycyjne nie są rzadkie. Jeśli $12 jest ustawione, możesz być pewien, że {[31] } też jest ustawione. (Może być ustawiony na pusty łańcuch, ale $# nie będzie mniejszy niż 12.) Jeśli "${arr[12]}" jest ustawione, nie ma gwarancji, że "${arr[11]}" jest ustawione, a długość tablicy może być tak mała, jak 1.
  2. element zerowy tablicy jest jednoznacznie elementem zerowym tej tablicy. W parametrach pozycyjnych zerowanie element nie jest pierwszym argumentem , ale nazwą powłoki lub skryptu powłoki.
  3. do shift tablicy, musisz ją pokroić i ponownie przypisać, jak arr=( "${arr[@]:1}" ). Można też zrobić unset arr[0], ale to czyniłoby pierwszy element o indeksie 1.
  4. tablice mogą być współdzielone między funkcjami powłoki jako globale, ale trzeba jawnie przekazać parametry pozycyjne do funkcji powłoki, aby je zobaczyć.

Często wygodne jest używanie rozszerzeń pathname do tworzenie tablic nazw plików:

$ dirs=( */ )

Komendy

Komendy są kluczowe, ale są one również bardziej szczegółowe niż instrukcja. Przeczytaj sekcję SHELL GRAMMAR. Różne rodzaje poleceń to:

  1. proste polecenia (np. $ startx)
  2. Rurociągi (np. $ yes | make config) (lol)
  3. listy (np. $ grep -qF foo file && sed 's/foo/bar/' file > newfile)
  4. polecenia złożone (np. $ ( cd -P /var/www/webroot && echo "webroot is $PWD" ))
  5. Koprocesy (złożone, bez przykładu)
  6. Functions (a named compound command that może być traktowany jako proste polecenie)

Model Wykonania

Model wykonania oczywiście obejmuje zarówno stertę, jak i stos. Jest to endemiczne dla wszystkich programów uniksowych. Bash posiada również stos wywołań funkcji powłoki, widoczny poprzez zagnieżdżone użycie wbudowanego caller.

Bibliografia:

  1. sekcja SHELL GRAMMAR podręcznika bash
  2. język poleceń powłoki XCU dokumentacja
  3. The Bash Guide on Greycat ' s wiki.
  4. Zaawansowane programowanie w środowisku UNIX

Proszę o komentarze, jeśli chcesz, żebym dalej rozwijał się w określonym kierunku.

 105
Author: kojiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 12:36:24

ODPOWIEDŹ na twoje pytanie " Czym jest dyscyplina typowania, np. czy wszystko jest ciągiem znaków" Zmienne Bash są ciągami znaków. Ale Bash umożliwia operacje arytmetyczne i porównywanie zmiennych, gdy zmienne są liczbami całkowitymi. Wyjątek od reguły zmienne Bash są ciągami znaków, gdy wspomniane zmienne są wpisane lub zadeklarowane w inny sposób

$ 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

Zadeklaruj znaczenie opcji:

  • -zmienna jest tablicą.
  • - F używa tylko nazw funkcji.
  • - i The zmienna jest traktowana jako liczba całkowita; ocena arytmetyczna jest wykonywana, gdy zmiennej przypisano wartość.
  • -p wyświetla atrybuty i wartości każdej zmiennej. Gdy użyto opcji-p, dodatkowe opcje są ignorowane.
  • - r sprawia, że zmienne są tylko do odczytu. Zmienne te nie mogą być następnie przypisane wartości przez kolejne polecenia assignment, ani nie mogą być wyłączone.
  • - T nadaje każdej zmiennej atrybut trace.
  • - x zaznacz każdą zmienną do eksportu do kolejnych polecenia poprzez środowisko.
 5
Author: Keith Reynolds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-22 00:53:03

Strona podręcznika bash ma nieco więcej informacji niż większość stron podręcznika i zawiera niektóre z tego, o co prosisz. Moje założenie po ponad dekadzie skryptowania bash jest to, że ze względu na jego ' historię jako rozszerzenie sh, ma trochę funky składni(w celu zachowania wstecznej kompatybilności z sh).

FWIW, moje doświadczenie było podobne do twojego; chociaż różne książki (np. O 'Reilly" Learning The Bash Shell " i podobne) pomagają w składni, istnieje wiele dziwnych sposobów rozwiązywania różne problemy, a niektóre z nich nie są w książce i muszą być wygooglowane.

 1
Author: philwalk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-21 23:59:00