Talen en automaten/2011-12/producten/Bash
Bash
Matjah Sonneveld, Gerco van Heerdt
Talen en automaten 2011-12
13:30 B |
Beschrijving
Bash (Bourne-again shell) werd in 1989 door GNU uitgebracht als vervanging van sh (Bourne shell). Het is de standaard (login) shell op veel besturingssystemen. Het wordt gebruikt om via de commandline de computer te besturen, meestal met simpele commando's die vooral externe programma's aanroepen, maar het is ook mogelijk om er hele programma's (scripts) mee te maken. De taal bevat namelijk dezelfde control-structuren die we in de meeste programmeertalen aantreffen. Wel wijkt de syntax op veel punten nogal af van die van talen die op C lijken. Omdat die soms ook nogal omslachtig is, zijn geavanceerdere scripts moeilijk leesbaar, zeker voor degenen die de taal niet goed kennen. Zulke scripts worden tegenwoordig dan ook vaker in andere talen geschreven.
We hebben voor deze taal gekozen omdat we op er dit moment alleen hele simpele gedeeltes van gebruiken. We willen tijdens het maken van deze casus graag leren welke mooie constructies er allemaal door geaccepteerd worden.
Voorbeelden:
<source lang="bash"> echo Hello World </source>
<source lang="bash"> for countdown in 5 4 3 2 1 do
echo $countdown
done </source>
Het volgende script loopt over alle bestanden in een map en zijn submappen, telt deze, laat hun naam zien en eventueel waar ze standaard mee uitgevoerd zouden worden als ze uitgevoerd zouden worden en hernoemt ondertussen .htm naar .html bestanden (ik verzin ook maar wat).
<source lang="bash">
- !/bin/bash
files=0 directories=(.)
for (( i=0; i < ${#directories[@]}; i=i+1 )) do
dir=${directories[$i]}/ echo Entering $dir for file in "$dir"* do if !($file =~ \*$) then if [ -d "$file" ] then directories[${#directories[@]}]=$file else if $file =~ \.htm$ then echo "$file -> $file"l mv $file $file'l' else echo $file`head -n1 "$file" | grep ^#! | sed 's/#!/: /'` fi files=$(($files + 1)) fi fi done
done
unset directories[@]
echo Total files: $files </source>
Merk op dat de eerste regel, die door de taal als commentaar wordt gezien, aangeeft waar de interpreter zich zou moeten bevinden.
Meer informatie is te vinden in de wiki[1] of de man pagina.[2]
Regulier?
Neem aan dat Bash regulier is en zij Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle k} de grenswaarde van het pomplemma. Laat Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle z} de string (die in Bash zit, zie de volgende sectie) (if ((rm -r /) then)Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle ^k} reboot; (fi)Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle ^k} zijn. Om het overzichtelijk te houden definieren we de strings Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle p, q} en Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle r} even als
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle p} = if (rm -r /) then
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle q} = reboot;
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle r} = fi
Dan kunnen we Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle z} schrijven als Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle uvw} , zo dat dat voldoet aan de criteria van het pomplemma. Dan gelden:
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle u = p^i}
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle v = p^j}
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle w = p^{k-i-j} qr^k}
waarbij Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle i + j <= k} en Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle j > 0} . Als we Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle v} pompen krijgen we Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle uv^{l}w} , wat volgens het pomplemma in Bash moet zitten, dus ook
Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle uv^{2}w} : Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle uv^{2}w = p^i p^{2j} p^{k-i-j}qr^k = p^{k+j}qr^k}
en die zit niet in Bash, omdat er Parsen mislukt (MathML met SVG- of PNG-terugval (aanbevolen voor moderne browsers en toegankelijkheidshulpmiddelen): Ongeldig antwoord ("Math extension cannot connect to Restbase.") van server "https://en.wikipedia.org/api/rest_v1/":): {\displaystyle j} if-statements niet af worden gesloten. Volgens het pomplemma moest deze string wel in Bash zitten, dus was onze aanname incorrect en is Bash geen reguliere taal.
Contextvrij?
Om aan te tonen dat Bash contextvrij is moeten we er een PDA of grammatica voor construeren. We hebben gekozen voor een grammatica. Als bron hiervoor gebruikten we de man pagina van Bash, met name het gedeelte onder Shell Grammar.[3]
Omdat de taal toch behoorlijk uitgebreid bleek te zijn hebben we een aantal dingen overgeslagen. Zo zou WORD ook delen met quotes mogen bevatten ('re'bo"ot" doet bijvoorbeeld hetzelfde als reboot) en hebben we dingen als arrays, comments, MATHEXPRESSION, de niet-alphanumerieke tekens bij PARAMETER, het niet-recursieve gedeelte van EVALEXPRESSION en nog een aantal andere dingetjes overgeslagen. Verder doen we alsof alle mogelijke commando- en bestandsnamen geldig zijn en beschouwen we ingebouwde commando's (bijvoorbeeld time) die als externe commando's geïmplementeerd zouden kunnen worden als externe commando's. Ook gaan we ervan uit dat we de betekenis van SPACE, TAB en NEWLINE niet hoeven te beschrijven. Eigenlijk hebben we geen Bash-specifieke dingen besproken, dus hadden we net zo goed sh kunnen nemen.
Terminals zijn alle combinaties van tekens behalve |, tenzij die direct tegen een (non-)terminal of een andere | aanstaat of er nog geen (non-)terminals gegeven zijn voor de huidige productieregel, die maximaal één hoofdletter bevatten. Om het zo exact mogelijk, maar toch nog een beetje overzichtelijk te houden: een spatie tussen een terminal en een non-terminal of twee terminals betekent dat daar een METALIST staat.
Het startsymbool is PROGRAM.
Main
PROGRAM → LIST RESTART | FUNCTION RESTART | λ RESTART → END PROGRAM SUBPROGRAM → LIST END | LIST END SUBPROGRAM LIST → SIMPLELIST | COMPOUND | METALIST SIMPLELIST → SIMPLE | ! META SIMPLE | SIMPLE && SIMPLELIST | SIMPLE || SIMPLELIST | SIMPLE ; SIMPLELIST | SIMPLE | SIMPLE | SIMPLE REDIRECTION | λ SIMPLE → VARIABLES META COMMAND META PIPES COMPOUND → SUB | GROUP | MATH | EVAL | FORLIST | FORLOOP | SELECT | CASE | IF | WHILE | UNTIL
Simple
VARIABLES → VARIABLE META VARIABLES | λ VARIABLE → VARIABLENAME=OPTIONALWORD VARIABLENAME → ALPHA ALPHANUMLIST COMMAND → COMMANDNAME META PARAMETERLIST PARAMETERLIST → PARAMETER PARAMETERLIST | λ PARAMETER → ALPHANUM | PARAMETER WORDLIST → WORD WORDLIST | λ OPTIONALWORD → WORD | λ COMMANDNAME → PARAMETER FILENAME → PARAMETER WORD → ALPHANUM ALPHANUMLIST ALPHA → a | ... | z | A | ... | Z | _ ALPHANUMLIST → ALPHANUM ALPHANUMLIST | λ ALPHANUM → ALPHA | NUM NUMLIST → NUM NUMLIST | λ NUM → 0 | ... | 9 PIPES → PIPE META PIPES | λ PIPE → | | |&
Compound
SUB → ( SUBPROGRAM ) GROUP → { META SUBPROGRAM END META } MATH → (( MATHEXPRESSION )) EVAL → [[ META EVALEXPRESSION META ]] EVALEXPRESSION → (EVALEXPRESSION) | !EVALEXPRESSION | EVALEXPRESSION && EVALEXPRESSION | EVALEXPRESSION || EVALEXPRESSION FORLIST → for META VARIABLENAME META WORDLIST OPTIONALEND do SUBPROGRAM END done FORLOOP → for (( MATHEXPRESSION ; MATHEXPRESSION ; MATHEXPRESSION )) DO SELECT → select META VARIABLENAME META INWORD DO DO → END do META SUBPROGRAM END done INWORD → in META WORDLIST | λ CASE → case META WORD META in META CASELIST META END esac CASELIST → OPTIONALLEFTPARENTHESIS PATTERNLIST SUBPROGRAM CASEENDING | λ OPTIONALLEFTPARENTHESIS → ( | λ CASEENDING → REALCASEENDING CASELIST | λ REALCASEENDING → ;; | ;& | ;;& IF → if SUBPROGRAM END then SUBPROGRAM END ELIFLIST ELSE fi ELIFLIST → elif SUBPROGRAM END then SUBPROGRAM END ELIFLIST | λ ELSE → else SUBPROGRAM END | λ WHILE → while LOOP UNTIL → until LOOP LOOP → SUBPROGRAM END do SUBPROGRAM END done
Other
FUNCTION → FUNCTIONDEFNAME COMPOUND REDIRECTION FUNCTIONDEFNAME → NAME PARENTHESES | function NAME OPTIONALPARENTHESES OPTIONALPARENTHESES → PARENTHESES | λ PARENTHESES → ( ) REDIRECTION → FILEDESCRIPTOR REDIRECTIONOPERATOR FILEDESCRIPTOR FILENAME REDIRECTION | λ FILEDESCRIPTOR → NUMLIST REDIRECTIONOPERATOR → > | < | >& | <& | >&- | <&- OPTIONALEND → END | λ END → NEWLINE | ; METALIST → META METALIST | λ META → SPACE | TAB | NEWLINE
Hiermee hebben we aangetoond dat Bash een contextvrije taal is, mits het gedeelte wat we niet hebben beschreven ook contextvrij is, omdat contextvrije talen gesloten zijn onder ∪.