Er der fejl eller mangler på siden? Så skriv gerne til hacker(snabel-a)matfystutor.dk

BASH Scripting

Fra Hackerwiki
Spring til navigation Spring til søgning

Bash (Bourne-Again SHell) scripting er en god ting at kunne, for det er rigtig brugbart. Det er lidt det samme som batch-scripting på Windows®, altså det at lave .bat filer, men bare med en anden syntax. Det kan i sidste ende spare dig en hel del tid når du arbejder med større projekter, og/eller bare skal køre de samme kommandoer mange gange fx kopiere .class filer til Tomcat mappe, og genstarte tomcat.

At starte simpelt

Ethvert script skal starte med en linie, der oplyser hvilket program scriptet skal køres med, i det her tilfælde /bin/bash:

  #!/bin/bash

De efterfølgende linier indeholder så kommandoer, fx:

  #!/bin/bash
  echo "Hello World!"

Det ovenstående skriver "Hello World!" ud til konsollen.

Man kan også definere variabler. Det nedenstående svarer til det ovenstående:

  #!/bin/bash
  hej="Hello World!"
  echo $hej

Man kan også tage output fra en anden kommando og bruge som variabel:

  #!/bin/bash
  hej="Hello $(whoami)!"
  echo $hej

Det ovenstående tager output fra whoami, som er dit brugernavn, og skriver Hello foran. Outputtet bliver: Hello brugernavn!

Men det er jo ikke mit navn, tænker du så, og nej det har du ret i. Man kan derfor tage imod input fra brugeren med read kommandoen:

  #!/bin/bash
  echo Indtast dit navn:  
  read hej
  echo "Hello $hej!"

Nogle gange vil man tilføje ekstra bogstaver til en variabel fx:

  #!/bin/bash  
  banan="banan";
  echo "$banan i flertal er ${banan}er".

Man skal i dette tilfælde omringe variabelnavnet med ${}. Dette skyldes at bash ellers ville lede efter en variabel der hed $bananer og give en runtime-fejl, da denne ikke fandtes.

Boolean værdier og tests

I linux afslutter alle programmer med en return code. Bash ser return code 0 som sand, og alt andet som falskt.

  $ echo "Hello World!" | grep Hello
  Hello World!
  $ echo $?
  0

echo $? ser hvad den sidste return code var, og udskriver den. I dette tilfælde returnerede grep 0, hvilket betyder at strengen "Hello" fandtes i "Hello World!"

test kommandoen bruges til at evaluere simple boolean udtryk, fx:

  $ test 10 -lt 5             #Tester om 10 er mindre end (less than) 5
  $ echo $?             
  1                           #Det er det ikke, da return code er forskellig fra 0
  $ test -n "Hello World!"    #Tester om strengen "Hello World!" har en non-zero længde (længde der ikke er nul)
  $ echo $?
  0                           #Det har den, da return code er 0

test har et usædvanligt alias, nemlig [. Denne notation skal dog afsluttes med en ]

  $ [ 10 -lt 5 ]
  $ echo $?
  1

Husk på, at der skal være mellemrum mellem de forskellige argumenter, for ellers fejler det:

  $ [ 5 -lt 4]          #Ingen mellemrum mellem 4 og ]
  bash: [: missing ']'

Argumenter for test kommandoen, og forklaringer:

Fil tests Forklaring
-d fil Er fil en mappe
-f fil Er fil en almindelig fil
-L fil Er fil et symbolsk link
-r fil Eksisterer fil og er den læsbar
-w fil Eksisterer fil og er den skrivbar
-x fil Eksisterer fil og er den eksekverbar
-s fil Eksisterer fil og er dens størrelse forskellig fra 0
fil1 -nt fil2 Er fil1 nyere end fil2
fil1 -pt fil2 Er fil1 ældre end fil2


Streng tests Forklaring
s1 = s2 Streng s1 er lig med s2
s1 != s2 Streng s1 er forskellig fra s2
-z s1 Streng s1 har længde 0
-n s1 Streng s1 har længde forskellig fra 0


Numeriske tests Forklaring
a -eq b Heltal a er lig med b
a -ne b Heltal a er forskellig b
a -gt b Heltal a større end b
a -ge b Heltal a større eller lig med b
a -lt b Heltal a mindre end b
a -le b Heltal a mindre eller lig med b


Kombinering af tests Forklaring
t1 -a t2 AND: Både t1 OG t2 er sande
t1 -o t2 OR: Enten t1 ELLER t2 er sand
! din_test Udeluk en test, her bliver din_test falsk
\( din_test \) Paranteser bruges, som i matematik, til at gruppere udtryk


bash har indbyggede kommandoer true og false, som returnerer hhv. 0 og 1:

  $ true
  $ echo $?
  0
  $ false
  $ echo $?
  1

Disse vil være brugbare når der skal laves if-else sætninger.


If sætningen

If sætningen vælger imellem en vilkårlig mængde alternativer, hvor hver af dem også kan indeholde en eller flere if sætninger. Den simpleste if-sætning er en if-then:

  #!/bin/bash
  if [ $(whoami) = "root" ]           #Hvis return code er 0
  then
      echo "Du er superbrugeren"      #Udfør dette kode
  fi

Så er der den lidt mere komplekse if-then-else:

  #!/bin/bash
  if [ $(whoami) = "root" ]          
  then
      echo "Du er superbrugeren"     
  else 
      echo "Du er en aldmindelig gut"
  fi

Bemærk at if afsluttes med fi, som er det omvendte af if.

Til sidst har vi if-then-elif-else sætningen, hvor der selvfølgelig kan forekomme vilkårligt mange elif imellem:

  #!/bin/bash
  if [ $(whoami) = "root" ]          
  then
      echo "Du er superbrugeren"     
  elif [ "$USER" = root ]
      echo "Du er måske superbrugeren"
  elif [ "$bestikkelse" -gt 10000 ]
      echo "Du kan betale dig til superbruger status"
  else
      echo "Du er bare en almindelig gut"
  fi


case sætningen

Case er lidt ligesom if, men den tester kun forskellige tilstande af én boolean, hvor if kan teste flere forskellige.

Case kan være ret handy:

  #!/bin/bash
  echo 'Hvad vil du gerne lave?'
  read svar
  case "$svar" in
    spise)
      echo "OK, tag en hamburger"
      ;;
    sove)
      echo "OK, godnat så"
      ;;
    *)
      echo "Jeg er ikke sikker på hvad du vil lave"
      echo "Vi ses nok i morgen"
   esac

Bemærk at case afsluttes med esac, som if gjorde med fi. Den generelle form ser sådan her ud:

   case $streng in
      udtryk1)
        body1
        ;;
      udtryk2)
        body2
        ;;
      ....
      udtrykN)
        bodyN
        ;;
      *)
        body_else
        ;;
   esac

Hvor $streng er en variabel af en art, og udtryk1...udtrykN er udtryk strengen testes på. Hvert udtryk har et tilhørende body, dvs. kode der bliver udført, og denne skal afsluttes med ;; som vist foroven. Den sidste del *) er ligesom en else for en if-sætning, det er det, der bliver udført, hvis $streng ikke passer i nogle af udtrykkene. Et andet eksempel følger:

   case $bogstav in
     X)
       echo "Bogstavet er et X"
       ;;
     [aeiouøæå])
       echo "Bogstavet er en vokal"
       ;;
     [0-9])
       echo "Bogstavet er et ciffer"
       ;;
     *)
       echo "Jeg ved ikke hvad bogstavet er"
   esac

Løkker

While-løkken gentager et sæt af kommandoer så længe et udtryk er sandt.

  while udtryk    #Så længe udtrykket er sandt (kommandoen returnerer 0)
  do
    body
  done

Bemærk at løkker altid afsluttes med done

Betragt eksemplet herunder. Scriptet hedder mit_script:

  #!/bin/bash
  i=0
  while [ $i -lt 3 ]
  do
    echo "$i"
    let "i += 1";    #Man kan også bruge i=$(($i+1))
  done

For at køre scriptet skal man sørge for det er eksekverbart,

  $ chmod +x mit_script     #Sørger for det er eksekverbart
  $ ./mit_script            #Kører scriptet
  0
  1 
  2


Der er også until løkken, som fortsætter med at gøre noget, indtil et udtryk bliver sandt:

  #!/bin/bash
  i=0
  until [ $i -ge 3 ]
  do
    echo "$i"
    let "i += 1"; 
  done

Kørslen af while og until eksemplet ser ens ud, da det er to måder at gøre det samme på.


Til sidst er der for-løkken, den itererer over en liste:

  for variabel in liste
  do
    body
  done

Betragt eksemplet:

  #!/bin/bash
  for name in Jørgen Jens Jonas
  do
    echo "$name er min ven"
  done

Ved kørsel ser det sådan ud:

  $./mit_script
  Jørgen er min ven
  Jens er min ven
  Jonas er min ven

For løkken er især brugbar når man skal iterere over filer:

  #!/bin/bash
  for file in *.doc
  do
    echo "$file er en træls Microsoft Word fil"
  done

Den ovenstående løkker finder alle .doc filer, og udskriver filnavn er en træls Microsoft Word fil.

Som et supplement til løkkerne er der break og continue.

Break kommandoen springer ud af den nuværende løkke:

  #!/bin/bash
  for name in Jørgen Jens Jonas
  do
    echo "$name er min ven"
    if [ "$name" = "Jens" ]
    then
       break
    fi
    echo "og"
  done
  echo "Færdig"

Kørslen ser således ud:

  $ ./mit_script
  Jørgen er min ven
  og
  Jens er min ven     #Her sker break-et, og echo "og" bliver ikke udført
  Færdig

Continue kommandoen derimod springer videre til næste iteration:

  #!/bin/bash
  for name in Jørgen Jens Jonas
  do
    echo "$name er min ven"
    if [ "$name" = "Jens" ]
    then
       continue
    fi
    echo "og"
  done
  echo "Færdig"

Kørslen ser således ud:

  $ ./mit_script
  Jørgen er min ven
  og
  Jens er min ven     #Her sker break-et, og echo "og" bliver ikke udført, da løkken springer videre
  Jonas er min ven    #Her er den sprunget over echo "og" og fortsat løkken
  og
  Færdig

At køre scripts

Som nævnt før, skal scriptet være eksekverbart før man kan køre det. Dette gøres med chmod +x sriptsti/scriptnavn kommandoen.

Ud over det skal bash scripts starte med #!/bin/bash før de kan eksekveres.

Man kan også køre scripts med bash:

   bash scriptnavn


Kommandolinie argumenter

Bash scripts kan læse kommandolinie argumenter ligesom fx java. I bash scriptet refereres der til de forskellige argumenter med $1, $2, $3 ... $n variablerne.

$0 er en speciel variabel, der indeholder navnet på scriptet fx hvis mit_script så således ud:

  #!/bin/bash
  echo "Scriptet hedder: $0"

Ville kørslen se sådan her ud:

  $./mit_script
  Scriptet hedder: ./mit_script

Antallet af argumenter gemmes i en tredje variabel: $#

Argumenter gemmes også i en liste: #@ så man kan iterere over dem:

  #!/bin/bash
  i=0
  for arg in $@
  do
     let "i += 1"
     echo "Argument $i : $arg";
  done 

En kørsel kunne se således ud:

  $ ./mit_script arg1 arg2 arg3
  Argument 1 : arg1
  Argument 2 : arg2
  Argument 3 : arg3

At afslutte med en Return Code

Man kan lade sine scripts afslutte med return codes, ligesom alle andre kommandoer i linux gør.

Dette gør man med udtrykket exit # hvor # skal erstattes med den return code man vil afslutte med:

  #!/bin/bash
  if [ $# -lt 2 ]
  then 
    echo "Fejl: Du skal skrive mindst to argumenter"
    exit 1
  else
    echo "Mit navn er $1 og jeg kommer fra $2"
  fi
  exit 0

Og return code kan ses, som nævnt tidligere i denne artikel:

  $ ./myscript Hacker
  Fejl: Du skal skrive mindst to argumenter
  $ echo $?
  1                                          #Return code var 1
  $ ./myscript Hacker CS
  Mit navn er Hacker og jeg kommer fra CS
  $ echo $?
  0                                          #Return code var 0