En shell er styresystemets kommandofortolker. Bash er min yndlingsskal, men alle Linux-shells fortolker de kommandoer, der indtastes af brugeren eller sysadmin, til en form, som operativsystemet kan bruge. Når resultaterne returneres til shellprogrammet, sender det dem til STDOUT, som som som standard viser dem i terminalen. Alle de shells, jeg kender, er også programmeringssprog.
Funktioner som f.eks. tabulatorkomplettering, genkaldelse og redigering af kommandolinjen og genveje som f.eks. aliaser bidrager alle til dens værdi som en kraftfuld shell. Dens standardtilstand til redigering af kommandolinjer bruger Emacs, men en af mine yndlingsfunktioner i Bash er, at jeg kan ændre den til Vi-tilstand for at bruge redigeringskommandoer, der allerede er en del af min muskelhukommelse.
Hvis man imidlertid tænker på Bash udelukkende som en shell, går man glip af en stor del af dens sande styrke. Under research til mit tre-binds Linux-selvstudiekursus i Linux (som denne artikelserie er baseret på) lærte jeg ting om Bash, som jeg aldrig havde vidst i over 20 års arbejde med Linux. Nogle af disse nye bidder af viden vedrører brugen af det som programmeringssprog. Bash er et kraftfuldt programmeringssprog, som er perfekt designet til brug på kommandolinjen og i shell-scripts.
Denne serie i tre dele udforsker brugen af Bash som et programmeringssprog til kommandolinjen (CLI). I denne første artikel gennemgås noget simpel kommandolinjeprogrammering med Bash, variabler og kontroloperatorer. De andre artikler udforsker typer af Bash-filer; streng-, numeriske og diverse logiske operatorer, der giver logik til styring af udførelsesflowet; forskellige typer af shell-udvidelser; og for-, while- og until-løsninger, der muliggør gentagne operationer. De vil også se på nogle kommandoer, der forenkler og understøtter brugen af disse værktøjer.
Shellen
En shell er styresystemets kommandofortolker. Bash er min yndlingsshell, men alle Linux-shells fortolker de kommandoer, der indtastes af brugeren eller systemadministratoren, til en form, som operativsystemet kan bruge. Når resultaterne returneres til shellprogrammet, viser det dem i terminalen. Alle de shells, jeg kender, er også programmeringssprog.
Bash står for Bourne Again Shell, fordi Bash-shell’en er baseret på den ældre Bourne-shell, som blev skrevet af Steven Bourne i 1977. Der findes mange andre skaller, men disse er de fire, jeg støder på mest hyppigt:
- csh: C-shell for programmører, der kan lide syntaksen i C-sproget
- ksh: Korn-shell, skrevet af David Korn og populær blandt Unix-brugere
- tcsh: En version af csh med flere brugervenlige funktioner
- zsh: En version af csh med flere brugervenlige funktioner
- zsh: Z-shell, som kombinerer mange funktioner fra andre populære shells
Alle shells har indbyggede kommandoer, som supplerer eller erstatter dem, der leveres af de centrale hjælpeprogrammer. Åbn shellens man-side og find afsnittet “BUILT-INS” for at se de kommandoer, den indeholder.
Hver shell har sin egen personlighed og syntaks. Nogle vil fungere bedre for dig end andre. Jeg har brugt C-shell, Korn-shell og Z-shell. Jeg kan stadig bedre lide Bash-shell’en end nogen af dem. Brug den, der fungerer bedst for dig, selv om det måske kræver, at du prøver nogle af de andre. Heldigvis er det ret nemt at skifte shells.
Alle disse shells er programmeringssprog, samt kommandotolkeprogrammer. Her er en hurtig rundvisning i nogle programmeringskonstruktioner og værktøjer, der er integrerede dele af Bash.
Bash som programmeringssprog
De fleste systemadministratorer har brugt Bash til at udsende kommandoer, der normalt er ret enkle og ligetil. Men Bash kan gå ud over at indtaste enkelte kommandoer, og mange sysadmins opretter enkle kommandolinjeprogrammer til at udføre en række opgaver. Disse programmer er almindelige værktøjer, der kan spare tid og kræfter.
Mit mål, når jeg skriver CLI-programmer, er at spare tid og kræfter (dvs. at være den dovne sysadmin). CLI-programmer understøtter dette ved at liste flere kommandoer i en bestemt rækkefølge, der udføres efter hinanden, så du ikke behøver at holde øje med forløbet af en kommando og indtaste den næste kommando, når den første er færdig. Du kan gå ud og lave andre ting og behøver ikke hele tiden at overvåge forløbet af hver enkelt kommando.
Hvad er “et program”?
The Free On-line Dictionary of Computing (FOLDOC) definerer et program som: “De instruktioner, der udføres af en computer, i modsætning til den fysiske enhed, som de kører på.” Princeton University’s WordNet definerer et program som: “…en sekvens af instruktioner, som en computer kan fortolke og udføre…” Wikipedia har også en god post om computerprogrammer.
Derfor kan et program bestå af en eller flere instruktioner, der udfører en specifik, beslægtet opgave. En instruktion til et computerprogram kaldes også en programerklæring. For sysadmins er et program normalt en sekvens af shell-kommandoer. Alle de shells, der er tilgængelige til Linux, i hvert fald dem jeg kender, har i det mindste en grundlæggende form for programmeringsmulighed, og Bash, standard-shellen i de fleste Linux-distributioner, er ingen undtagelse.
Selv om denne serie bruger Bash (fordi den er så allestedsnærværende), vil de generelle programmeringsbegreber være de samme, hvis du bruger en anden shell, selv om konstruktionerne og syntaksen kan være noget anderledes. Nogle shells understøtter måske nogle funktioner, som andre ikke gør, men de giver alle en vis programmeringsmulighed. Shell-programmer kan gemmes i en fil til gentagen brug, eller de kan oprettes på kommandolinjen efter behov.
Enkle CLI-programmer
De enkleste kommandolinjeprogrammer er en eller to på hinanden følgende programerklæringer, som kan være relaterede eller ej, og som indtastes på kommandolinjen, før Enter-tasten trykkes ned. Det andet udsagn i et program, hvis der er et, kan være afhængigt af handlingerne i det første, men det behøver det ikke at være.
Der er også en smule syntaktisk tegnsætning, der skal angives klart og tydeligt. Når man indtaster en enkelt kommando på kommandolinjen, afsluttes kommandoen ved at trykke på Enter-tasten med et implicit semikolon (;). Når det bruges i et CLI-shellprogram, der indtastes som en enkelt linje på kommandolinjen, skal semikolonet bruges til at afslutte hvert udsagn og adskille det fra det næste. Den sidste erklæring i et CLI-shellprogram kan bruge et eksplicit eller implicit semikolon.
Nogle grundlæggende syntaks
De følgende eksempler vil tydeliggøre denne syntaks. Dette program består af en enkelt kommando med et eksplicit terminator:
$ echo "Hello world." ;
Hello world.
Det virker måske ikke som et særligt omfattende program, men det er det første program, jeg støder på i hvert nyt programmeringssprog, jeg lærer. Syntaksen er måske en smule anderledes for hvert sprog, men resultatet er det samme.
Lad os uddybe dette trivielle, men allestedsnærværende program lidt. Dine resultater vil være anderledes end mine, fordi jeg har lavet andre eksperimenter, mens du måske kun har de standardmapper og -filer, der oprettes i kontoens hjemmemappe, første gang du logger ind på en konto via GUI-skrivebordet.
$ echo "My home directory." ; ls ;
My home directory.
chapter25 TestFile1.Linux dmesg2.txt Downloads newfile.txt softlink1 testdir6
chapter26 TestFile1.mac dmesg3.txt file005 Pictures Templates testdir
TestFile1 Desktop dmesg.txt link3 Public testdir Videos
TestFile1.dos dmesg1.txt Documents Music random.txt testdir1
Det giver lidt mere mening. Resultaterne hænger sammen, men de enkelte programerklæringer er uafhængige af hinanden. Bemærk, at jeg kan lide at sætte mellemrum før og efter semikolonet, fordi det gør koden lidt lettere at læse. Prøv det lille CLI-program igen uden et eksplicit semikolon til sidst:
$ echo "My home directory." ; ls
Der er ingen forskel i resultatet.
Noget om variabler
Som alle programmeringssprog kan Bash-shell’en håndtere variabler. En variabel er et symbolsk navn, der henviser til en bestemt placering i hukommelsen, som indeholder en værdi af en eller anden art. Værdien af en variabel kan ændres, dvs. den er variabel.
Bash typebestemmer ikke variabler som C og beslægtede sprog, der definerer dem som hele tal, flydende punkter eller strengtyper. I Bash er alle variabler strings. En streng, der er et heltal, kan bruges i heltalsaritmetik, hvilket er den eneste type matematik, som Bash er i stand til at udføre. Hvis der er behov for mere kompleks matematik, kan kommandoen bc bruges i CLI-programmer og -scripts.
Variabler tildeles værdier og kan bruges til at henvise til disse værdier i CLI-programmer og -scripts. Værdien af en variabel angives ved hjælp af dens navn, men ikke med et $-tegn foran. Tildelingen VAR=10 sætter værdien af variablen VAR til 10. Hvis du vil udskrive værdien af variablen, kan du bruge udsagnet echo $VAR. Begynd med tekstvariabler (dvs. ikke-numeriske variabler).
Bash-variabler bliver en del af shell-miljøet, indtil de ikke længere er indstillet.
Kontroller den oprindelige værdi af en variabel, der ikke er blevet tildelt; den skal være nul. Tildel derefter en værdi til variablen, og udskriv den for at verificere dens værdi. Du kan gøre alt dette i et enkelt CLI-program:
$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;
Hello World
$
Bemærk: Syntaksen for tildeling af variabler er meget streng. Der må ikke være mellemrum på begge sider af lighedstegnet (=) i tildelingsangivelsen.
Den tomme linje angiver, at den oprindelige værdi af MyVar er nul. Ændring og fastsættelse af værdien af en variabel sker på samme måde. Dette eksempel viser både den oprindelige og den nye værdi.
Som nævnt kan Bash udføre aritmetiske beregninger med heltal, hvilket er nyttigt til at beregne en reference til placeringen af et element i et array eller til at lave enkle matematiske problemer. Det er ikke egnet til videnskabelige beregninger eller til noget, der kræver decimaler, f.eks. finansielle beregninger. Der findes langt bedre værktøjer til disse typer beregninger.
Her er en simpel beregning:
$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63
Hvad sker der, når du udfører en matematisk operation, der resulterer i et glidende tal?
Resultatet er det nærmeste hele tal. Bemærk, at beregningen blev udført som en del af ekko-anvisningen. Matematikken udføres før den omsluttende echobefaling på grund af Bash-rækkefølgen af præcedens. Du kan finde flere oplysninger på Bash-manualsiden og søge på “precedence.”
Kontroloperatører
Shell-kontroloperatører er en af de syntaktiske operatører, der gør det nemt at oprette nogle interessante kommandolinjeprogrammer. Den enkleste form for CLI-program er blot at kæde flere kommandoer sammen i en sekvens på kommandolinjen:
command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;
Disse kommandoer kører alle uden problemer, så længe der ikke opstår fejl. Men hvad sker der, når der opstår en fejl? Du kan forudse og tage højde for fejl ved hjælp af de indbyggede && og || Bash-kontroloperatorer. Disse to kontroloperatorer giver en vis flowkontrol og gør det muligt for dig at ændre sekvensen af kodeudførelsen. Semikolonet betragtes også som en Bash-kontroloperator, og det samme gælder newline-tegnet.
Operatoren && siger ganske enkelt: “Hvis kommando1 er vellykket, så kør kommando2. Hvis kommando1 fejler af en eller anden grund, så springes kommando2 over.” Denne syntaks ser således ud:
command1 && command2
Se nu på nogle kommandoer, der opretter en ny mappe og – hvis det lykkes – gør den til den nuværende arbejdsmappe (PWD). Sørg for, at din hjemmemappe (~) er PWD’en. Prøv først dette i /root, en mappe, som du ikke har adgang til:
Fejlen blev udsendt af mkdir-kommandoen. Du modtog ikke en fejl, der angiver, at filen ikke kunne oprettes, fordi oprettelsen af mappen mislykkedes. Kontroloperatoren && fornemmede, at returkoden ikke var nul, så touch-kommandoen blev sprunget over. Ved hjælp af kontroloperatoren && forhindres det, at touch-kommandoen køres, fordi der var en fejl ved oprettelsen af mappen. Denne form for strømningskontrol af kommandolinjeprogrammer kan forhindre, at fejl bliver forværret og skaber et stort rod i tingene. Men det er på tide at blive lidt mere kompliceret.
Med kontroloperatoren || kan du tilføje en anden programanvisning, der udføres, når den oprindelige programanvisning returnerer en kode større end nul. Den grundlæggende syntaks ser således ud:
command1 || command2
Denne syntaks lyder: “Hvis kommando1 mislykkes, skal du udføre kommando2.” Det indebærer, at hvis kommando1 lykkes, springes kommando2 over. Prøv dette ved at forsøge at oprette en ny mappe:
Det er præcis, hvad du ville forvente. Da den nye mappe ikke kunne oprettes, mislykkedes den første kommando, hvilket resulterede i, at den anden kommando blev udført.
Kombinationen af disse to operatorer giver det bedste af begge dele. Syntaksen for kontroloperatorer, der anvender en vis flowkontrol, har denne generelle form, når kontroloperatorerne && og || anvendes:
preceding commands ; command1 && command2 || command3 ; following commands
Denne syntaks kan angives på følgende måde: “Hvis kommando1 afsluttes med en returkode på 0, skal kommando2 udføres, ellers skal kommando3 udføres.” Prøv det:
Prøv nu den sidste kommando igen ved at bruge din hjemmemappe i stedet for /root-mappen. Du har tilladelse til at oprette denne mappe:
$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
$
Syntaksen for kontroloperatorer, som command1 && command2, fungerer, fordi hver kommando sender en returkode (RC) til shell’en, der angiver, om den blev gennemført med succes, eller om der var en eller anden form for fejl under udførelsen. Konventionelt angiver en RC på nul (0) en succes, og ethvert positivt tal angiver en eller anden form for fejl. Nogle af de værktøjer, som sysadmins bruger, returnerer blot et et (1) for at angive en fejl, men mange bruger andre koder til at angive den type fejl, der opstod.
Bash-skalvariablen $? indeholder RC’en fra den sidste kommando. Denne RC kan meget nemt kontrolleres af et script, den næste kommando i en liste af kommandoer eller endda direkte af sysadmin’en. Start med at køre en simpel kommando og kontrollér straks RC’en. RC’en vil altid være for den sidste kommando, der blev kørt, før du kiggede på den.
RC’en er i dette tilfælde nul, hvilket betyder, at kommandoen blev afsluttet med succes. Prøv nu den samme kommando på roots hjemmemappe, en mappe, som du ikke har tilladelser til:
I dette tilfælde er RC’en to; det betyder, at tilladelsen blev nægtet for en ikke-root-bruger til at få adgang til en mappe, som brugeren ikke har tilladelse til at få adgang til. Kontroloperatorerne bruger disse RC’er til at give dig mulighed for at ændre rækkefølgen af programudførelsen.
Resumé
Denne artikel kiggede på Bash som programmeringssprog og udforskede dets grundlæggende syntaks samt nogle grundlæggende værktøjer. Den viste, hvordan man udskriver data til STDOUT, og hvordan man bruger variabler og kontroloperatorer. Den næste artikel i denne serie ser på nogle af de mange logiske Bash-operatorer, der styrer strømmen af instruktionsudførelsen.