Found at: http://linmag.no/article/articleprint/698/

Grunnleggende Bash-skripting


Å kunne skrive skript i et eller flere språk eller skall er utrolig nyttig. Flere har etterlyst en innføring i Bash-skripting og her tar jeg for meg det grunnleggende. Vi har valgt å legge nivået så lavt som mulig, slik at de fleste skal kunne ha nytte av det.

De fleste Linux-distribusjoner kommer med Bash, Bourne Again SHell, som standardskall. Bash er et sh-kompatibelt skall, og vil på en rekke distribusjoner også være en symbolsk lenke til /bin/sh. Hvis du aldri har startet en terminal eller logget inn med f. eks ssh på Linux-boksen din, er det nå på tide å gjøre det. Kjører du KDE eller Gnome, vil det sannsynligvis være et terminalikon på oppgavelinjen.

Bash er en kommandolinjetolker og mange tenker nok ikke på at man kan programmere den. I første del, vil vi se på hva det er som gjør at vi kan benytte den til skripting.
I eksemplene vil alle linjer som begynner med $ være et «prompt»; det som skallet skriver ut når det er klart til å ta imot en kommando. Andre eksempler på prompter kan være #, som vanligvis antyder at dette skallet kjøres som root. 

variable 
Muligens den viktigste egenskapen til programmeringsspråk, er evnen til å huske på data. Dette gjøres i det som kalles variable. Hvis du synes det høres rart ut, kan du forestille deg en variabel som en eske som du kan putte ting i, ta ting ut fra, se på osv. Hvis du så merker en eske for fornavn og en annen for etternavn og putter fornavn og etternavn i respektive esker, er det kanskje lettere å skjønne.
For å putte data i en variabel i Bash gjør du slik:
$ FORNAVN=”Balle”
$ ETTERNAVN=”Klorin”
Det er viktig at det ikke er mellomrom mellom variabelnavnet (FORNAVN) og =
For å benytte innholdet i en variabel bruker man så $variabelnavn:
$ echo “Hei, $FORNAVN $ETTERNAVN”
Hei, Balle Klorin
I skallet ditt er det alt satt en hel del variable. Disse variablene kalles for miljøvariable (Environment variables) og vil være tilgjengelige for alle programmer som blir startet i dette miljøet. En slik variabel vi kommer til å komme borti er $PATH. Den inneholder en liste over kataloger som det skal letes etter kommandoer i. 
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games
Når jeg forsøker å kjøre en kommando, vil skallet først sjekke i /usr/local/bin, finnes det et kjørbart program i den katalogen, kjøres det, hvis ikke sjekker den /usr/bin og alle de andre katalogene.
Vi kan selv legge til miljøvariable ved å eksportere dem. 
$ export FIRMA=”Supperådet”
I alle programmer som blir startet etterpå, vil da miljøvariablen $FIRMA være tilgjengelig.
Den innebygde kommandoen set vil, uten argumenter, liste ut alle miljøvariablene og litt til som er tilgjengelig i ditt kjørende skall
$ set
Bash har også støtte for array. Array er en litt annen type variabel. Hvis vi skal sammenligne med noe igjen, blir et array omtrent som en posthylle. Hvis vi tenker på det første rommet i hylla som rom 0 og det andre som rom 1 og videre oppover, har vi et array. Vi har altså en beholder som kan inneholde flere variable.
$ ARRAY[0]=»en»
$ ARRAY[1]=”to”
$ ARRAY[3]=”tre”
$ echo ${ARRAY[@]}
en to tre
$ echo ${ARRAY[2]}
to
$ FRUKT=(eple pære appelsin)
$ echo ${FRUKT[2]}
appelsin
Vi ser at vi kan enten tilordne data til hvert enkelt rom, eller vi kan tilordne flere ved hjelp av (og). Ble det rotete? Ikke fortvil, vi klarer oss veldig lenge med bare enkle variable.

kontrollstrukturer
Variable er en viktig byggestein og nå har vi kommet til en annen. «Kontrollstrukturer, hva i alle dager er det for slags ord?», tenker du kanskje. En kontrollstruktur er lettvint sagt en av to ting; tester eller gjentagelser. 
Etter hvert som du lærer deg flere programmeringsspråk, vil du oppdage at det ikke er så vanskelig å lære seg et nytt. Å programmere er ofte å teste og å gjenta; «Har jeg fått forventet resultat?», «For alle elementene i denne listen, gjør dette», etc. Selve språket blir derfor omtrent som en dialekt, med egen grammatikk og ordliste, mens setningsoppbyggingen er omtrent lik.

if, elif, fi
Ofte vil vi teste om vi har en eller annen form for status som gjør at vi skal velge det ene eller det andre. I Bash er dette på formen 
if test1; then
   echo “test1 er sann”
fi
Selve testen kan være om en kommando kjører og returnerer at det gikk greit, eller veldig ofte er det bruk av programmet test eller [ (disse er sidestilte og lar deg utføre en del nyttige tester).
if [ -z $FIRMA ]; then
   echo “Firmanavn mangler”
fi
if [ “$FORNAVN” = “Balle” -a “$ETTERNAVN” = “Klorin” ]; then
   FIRMA=”Supperådet”
fi
For fullstendig oversikt hva man kan gjøre med test/[, se på manualsiden til test (man test). Som oftest bruker vi = og != for å sjekke om en variabel er lik eller ulik en gitt streng. -a og -o er også ofte brukt for å kunne lage logiske rekker av tester, -a er og, og -o er eller; «dette må vare sant og dette må også være sant», «dette må være sant, eller dette må være sant».
Vi har ikke snakket om hvilke datatyper variable kan ha i Bash. I utgangspunktet kan du tenke deg at alle er tekst, eller strenger som det gjerne blir kalt i program-meringsverdenen. I tillegg finnes det en datatype for heltall. Det finnes da 6 tester for heltall; -eq er lik, -ge er større enn eller lik, -gt er større enn, -le er mindre enn eller lik, -lt er mindre enn og -ne er ulik.
if [ $TALL -ge 10 ]; then
   echo “Tallet er større enn eller lik 10”
elif [ $TALL –lt 5 -a $TALL -gt 0 ]; then
  echo “Tallet er mindre enn 5 og større enn 0”
fi
Ellers bruker vi test/[ til å kjøre tester på filer.
if [ ! -x $FIL ]; then
  echo “$FIL er ikke kjørbar”
fi
De vi stort sett bruker er; -f en vanlig fil, -r en lesbar fil, -w en skrivbar fil og -x en kjørbar fil. Det finnes en haug flere tester, men de kan du lese mer om i manualsiden til test.

for-løkker
Da vet vi hvordan vi tester, nå er det på tide å gjenta. Bash har flere gjentagelsesstrukturer, og for er den mest brukte av dem. Det finnes to, den ene mer brukt enn den andre. La oss se på den mest brukte først. I manualsiden står det for name [ in word ] ; do list ; done. Noe kryptisk, men ikke så vanskelig. La oss ta et eksempel.

$ for DYR in ku hest geit
> do
>   echo $DYR
> done
ku
hest
geit
Her skriver vi ikke inn >, det er noe Bash skriver ut når vi har begynt en kommando som ikke er avsluttet ennå. Altså, for tar listen ku hest geit og for hvert element i den listen setter den variabelen DYR og kjører den blokken mellom do og done. 
I stedet for en oppramsende liste, kan man bruke en variabel eller en variabelsubstitusjon som vi skal komme tilbake til litt senere. Er det en variabel, vil hvite tegn (mellomrom, tabulatorer og linjeskift) dele opp elementene. Man kan også bruke array.
$ for ELEMENT in ${FRUKT[@]}; do echo $ELEMENT; done
eple
pære
appelsin
Den andre for-løkkestrukturen beskrives slik i manualen; for (( expr1 ; expr2 ; expr3 )) ; do list ; done. For de av dere som kan andre programmeringsspråk, kjenner dere igjen denne. expr1 vil bli aritmetisk (fjongt ord for å regne matematisk) evaluert første gang, så vil man testet expr2 hver gang og om det er logisk rett vil blokken do .. done bli kjørt. Om det ble kjørt vil så expr3 bli utført.
$ MAX=4; for ((i=0; i <= $MAX; i++)); do echo $i; done
0
1
2
3
4
Vi kan altså lett lage oss strukturer hvor vi skal gjenta noe et gitt antall ganger.

while, until og break
En annen gjentagelsesstruktur er while; «så lenge dette er sant, gjør dette». 
$ while [ «$ANS» != «stopp» ]; do echo -n «Avslutte (skriv stopp)? «; read ANS; done
Avslutte (skriv stopp)? 
Avslutte (skriv stopp)? w4hui9
Avslutte (skriv stopp)? stopp
Så lenge vi ikke har fått beskjed om å stoppe, gjentar vi. Her ser vi forresten -n til echo og bruken av read. echo skriver alltid ut en ny linje, men om vi gir -n som parameter, så gjør den ikke det. read VAR venter til brukeren har trykket enter og legger det innskrevede resultatet i variabelen VAR.
while brukes av og til som en uendelig løkke. En uendelig løkke er en som aldri vil avsluttes fordi testen ikke vil bli usann. Med while gjør man det med enten å bruke kommandoen true eller den innebygde : i Bash.
while :; do clear; date; sleep 30; done
Dette blanker skjermen, skriver ut dato med klokke, venter i 30 sekunder og gjentar. Vi har lagd en enkel klokke.
Du ser kanskje ikke den helt store nytten i slike uendelige løkker, men når vi da lærer oss at break avbryter løkken som kjøres, har vi plutselig mer å jobbe med.
$ touch slutt
$ while :; 
> do 
>  clear; date; sleep 5; 
>  if [ -f “slutt” ]; then echo “takk for nå”; break; fi; 
> done
Mon Jan  5 12:01:50 CET 2004
takk for nå
Så lenge filen slutt ikke finnes der, så kjører klokken. 
Until er whiles omvendte funksjon. Den kjører helt til en test er sann i motsetning til while som kjører så lenge en test er sann.
$ until [ «$ANS» = «stopp» ]; do echo -n «Avslutte (skriv stopp)? «; read ANS; done
Avslutte (skriv stopp)? stopp

case
Vi har en variabel som kan ha en rekke forskjellige strenger, så kan vi i stede for å bruke en rekke med if .. elif .. elif .. fi bruke case. I en del andre språk blir dette kalt en switch.
while :; do 
> echo -n ‘¤ ‘; read SVAR; > case $SVAR in
> ja) 
>   echo «svaret er ja»
>   ;;
> nei)
>   echo «svaret er nei»
>   ;;
> slutt)
>   echo «avslutter»
>   break
>   ;;
> *)
>   echo $SVAR
>   ;;
> esac
> done
¤ test
test
¤ ja
svaret er ja
¤ slutt
avslutter
Vi leser inn i variabelen $SVAR med read. Så kjører vi en case på variabelen, og skriver ut litt forskjellige ting avhengig hva den inneholder. Vi kan kalle disse testene for mønstertester. Legg merke til * som passer til alle uttrykk, og derfor bør ligge som siste mønster. I tillegg kan man lage en eller-liste ved å bruke | mellom mønstrene.
$ case SVAR in
>   ja|nei)
>      echo “bestem deg nå”
>      ;;
> esac

innbygde funksjoner og programmer i $path
Tidligere så vi på miljøvariablen $PATH og fikk vite at Bash leter i katalogene etter kjørbare programmer. I tillegg har Bash en del innbygde, f. eks : som vi så på ved uendelige løkker. Jeg nevnte tidligere noe jeg kalte variabelsubstitusjon. Det er en fin måte å tilordne resultatet av en kommando til variabel. Dette gjør vi enten ved å legge “-tegn (det er shift og tasten til venstre for backspace) rundt kommandoen eller ved å legge $( ... ) rundt den. Jeg foretrekker den siste varianten. “-tegnet kan være vanskelig å skille fra andre tegn i enkelte fonter, og så kan man ha substitusjoner i substitusjoner om man bruker $( .. )
$ IP=“hostname -i“
$ echo $IP
127.0.0.1
$ IP=$(/sbin/ifconfig eth0 | grep addr: | cut -d: -f2 | cut -d” “ -f1)
$ echo $IP
129.241.172.88
Hva gjør jeg egentlig av magi i det andre eksemplet der? Jeg bruker |-tegnet, ofte kalt en pipe eller rør, og lager en pipeline. Resultatet av en kommando sendes videre til den neste. Om du kjører /sbin/ifconfig eth0, så ser du at det viser deg masse informasjon om nettkortet ditt. Dette dyttes så til grep, grep leter etter mønsteret addr: og skriver så ut den linja som passer til mønsteret. Dette dytter vi videre til cut to ganger. Den første gangen bruker vi : som skilletegn og den andre et mellomrom. På den måten får vi tak i bare IP-adressen.
Det er en innebygd funksjon som heter type, som kan være nyttig å kjenne til. 
$ type -a ls
ls is aliased to ‘ls —color=auto -CF’
ls is /bin/ls
Vi kan også lage våre egne innebygde kommandoer, eller funksjoner som det gjerne kalles.
function setenv() {
  =;
  export ;
}
Argumentene til funksjonen vil komme som ,  osv eller du kan få tak i alle i variabelen $@.
La oss lage vår egen funksjon som finner ut hvor en kommando er i $PATH
function ptype() { 
  FANT=0; 
  for DIR in $(echo $PATH | sed ‘s/:/ /g’); do 
    if [ -x “$DIR/” ]; then 
      echo “$DIR/”; 
      FANT=1; 
    fi; 
  done; 
  if [ $FANT -eq 0 ]; then echo «Fant ikke »; fi; 
}
$ ptype eefnwk
Fant ikke eefnwk
$ ptype ls    
/bin/ls
Av kronglerier her, ser vi at for-løkken får listen sin fra en substitusjon. Jeg skriver ut $PATH og sender den til sed. sed er en kommando som kan brukes til å søke å erstatte. Vi trenger $PATH oppdelt med hvite tegn så vi erstatter alle : med et mellomrom.

programmere i bash
Vi har lært oss de grunnleggende byggesteinene i Bash, og er klare til å begi oss ut i å skrive skript. Et Bash-skript er en tekstfil som inneholder Bash-kommandoer. Denne fila sier vi at vi kjører. For å få til det, må vi angi at det er et Bash-skript vi snakker om. Vanligvis gjør man det ved å skrive #!/bin/bash på første linje. Lag en fil som du kaller «heiverden.sh» (jeg vil ikke begi meg ut i editor-kriging så jeg nevner bare vi og emacs som alternativer)
I den fila skriver du inn dette:
#!/bin/bash
echo «Hei, verden!»
Gjør vi denne fila kjørbar med chmod, kan vi kjøre den:
$ chmod a+x heiverden.sh 
$ ./heiverden.sh 
Hei, verden!
En annen måte å kjøre et skript på, er å gi filnavnet som argument til Bash:
$ bash heiverden.sh
Hei, verden!
I Linux-verdenen er det ikke nødvendig å ha en spesiell filendelse på filene. Det kan likevel være nyttig for deg selv å bruke det, slik at du lett ser at heiverden.sh er et skallskript. 
Jeg har lagd meg en bin-katalog på hjemmeområdet mitt, og lagt til denne i miljøvariablen $PATH. Når jeg så har lagd et nyttig skript legger jeg så dette i denne katalogen. For å endre $PATH anbefaler jeg å sjekke ut filene .bashrc og .bash_profile i hjemmeområdet ditt. Hos meg har jeg følgende i .bash_profile:
export PATH=$HOME/perl:$HOME/bin:$PATH
Etter å ha jobbet en del med Skolelinux, har jeg brent meg et par ganger på å skrive Bash-skript og forsøkt å bruke /bin/sh til å kjøre det. På Skolelinux er nemlig /bin/sh ikke Bash og endel av de mer avanserte innebygde funksjonen i Bash er ikke nødvendigvis tilgjengelig i andre implementasjoner av /bin/sh. Lær deg derfor til å bruke /bin/bash når du skriver Bash-kode 




| Tilbake til vanlig sidevisning | Tips en venn om denne artikkelen |