Nighthawk.dk

Shellscripts

Skrevet d. 4. Oct. 2020 af Claus Nielsen.

Shellscripting giver mulighed for at skrive små selvstændige programmer, som kan bruges til alt muligt - f.eks. til at rense rådata så de kan analyseres.

Bruger man en række Bash-kommandoer ofte, kan man samle dem i et shellscript, som egentlig bare er et lille program, man kan køre. Lad os starte med at lave et super simpelt shellscript, for at se hvordan man gør:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
echo "Hello World!"
claus@ClausDesktop:~/work$ chmod 755 shellscript.sh
claus@ClausDesktop:~/work$ ./shellscript.sh
Hello World!

Dette shellscript udskriver bare "Hello World!" vha. echo-kommandoen, men bemærk den første linie i scriptet: #!/bin/bash. Denne linie bliver kaldt en shebang, og den fortæller bare at scriptet er et Bash-script; f.eks. kunne man også lave et Octave-shellscript ved at bruge en anden shebang (#!/usr/bin/env octave).

I et shellscript kan man skrive Bash-kommandoer, men man kan også meget mere end det.

Variabler og Parametre

Et shellscript fungerer som et lille program, og ligesom med programmer kan man sende parametre til programmet når man kører det fra terminalen. Her er et eksempel:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
echo "Hello $1!"
claus@ClausDesktop:~/work$ ./shellscript.sh Claus
Hello Claus!

Man kan altså tilgå parametre med $1, $2, $3, osv. og du kan se antallet af parametre i $#. Bemærk at parametrene er eksempler på variabler, som du måske kender fra andre programmeringssprog.

Man kan lave variabler i Bash ved at skrive NAVN=VÆRDI, hvor NAVN er navnet på variablen, og VÆRDI er f.eks. et tal eller en tekststreng angivet med anførselstegn ("tekst"). Man kan så senere bruge variablen ved at skrive $NAVN. Læg mærke til at parametrene derfor bare er variabler med navnene #, 1, 2, osv.

Her er et eksempel mere, der også definerer nogle variabler:

#!/bin/bash
FILEEXTENSION="txt"
OUTPUTFILE="$1.$FILEEXTENSION"

echo "Test!" > $OUTPUTFILE
echo "En anden linie skrevet til filen!" >> $OUTPUTFILE
echo "En tredje linie!" >> $OUTPUTFILE

LINIER=`cat $OUTPUTFILE | wc -l`
echo "Der blev skrevet $LINIER linier til filen $OUTPUTFILE!"

Definitionen af variablen LINIER er lidt speciel - ved at skrive Bash-kommandoer mellem ` og ` kan du gemme resultatet af en Bash-kommando i en variabel (bemærk at du godt kan bruge piping med |!). Ellers bliver der bare defineret nogle variabler, og de bliver brugt ved at skrive $ foran navnet. Hvis du kører scriptet skulle du gerne få følgende resultat:

claus@ClausDesktop:~/work$ ./shellscript.sh test
Der blev skrevet 3 linier til filen test.txt!
claus@ClausDesktop:~/work$ cat test.txt
Test!
En anden linie skrevet til filen!
En tredje linie!

Betingelser

Hvad nu hvis dit script kræver f.eks. tre parametre, og brugeren kun indtaster to? Det kan du tjekke vha. en betingelse eller if-sætning:

#!/bin/bash
if [ $# -eq 3 ]
then
		echo "Du har indtastet 3 parametre!"
else
		echo "Du har indtastet $# parametre, men du skulle indtaste 3!"
fi

Generelt kan du skrive if [ betingelse ] then ... fi, hvor betingelse er et udsagn, som f.eks. at antallet af parametre skal være lig med (equal, -eq) værdien 3, som i eksemplet ovenfor. Du kan også bruge større end (greater than, -gt) eller mindre end (less than, -lt) i stedet for -eq. Hvis betingelsen er sand, køres kommandoerne mellem then og fi, mens de ignoreres hvis betingelsen er falsk.

Man kan desuden bruge else (som i eksemplet ovenfor): hvis betingelsen er sand, køres koden mellem then og else, og hvis betingelsen er falsk køres i stedet koden mellem else og fi.

Loops

Hvis du skal gentage nogle kommandoer flere gange, kan du bruge loops. Prøv selv dette eksempel af:

#!/bin/bash
for i in 1 2 3 "Hund" "Kat" "Fisk" 42
do
		echo "i: $i"
done

for j in $(seq 1 10)
do
		echo "j: $j"
done

Når du bruger sådan et loop får du en variabel (her i eller j) som kun findes mellem do og done for det ene loop, og som skifter værdi hver gang kommandoerne mellem do og done bliver kørt (indtil variablen har haft alle de forskellige værdier, der blev bedt om, dvs. 1, 2, 3, Hund, Kat, Fisk og 42 i første loop).

Her er et andet eksempel:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
for i in $(seq $1 $2)
do
        echo "i: $i"
done
claus@ClausDesktop:~/work$ ./shellscript.sh 4 7
i: 4
i: 5
i: 6
i: 7

Aritmetik

Hvis du har brug for at lave en smule matematik - eller rettere aritmetik - kan du bl.a. gøre det vha. $ og dobbelt-paranteser: $(( ... )). Her er et eksempel:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
RESULTAT=$(($1 + $2))
echo "$1 + $2 = $RESULTAT"
claus@ClausDesktop:~/work$ ./shellscript.sh 4 7
4 + 7 = 11

Bemærk dog at du kun arbejder med heltal på denne måde. Hvis du i stedet skal bruge decimaltal er du nødt til at bruge programmer som f.eks. en Python-fortolker:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
RESULTAT=`python -c "print($1 + $2)"`
echo "$1 + $2 = $RESULTAT"
claus@ClausDesktop:~/work$ ./shellscript.sh 4.2 7.1
4.2 + 7.1 = 11.3

Heredoc

Nogle gange er Bash ikke nok, eller måske vil du bare gerne bruge f.eks. Python eller Octave til nogle beregninger, plotting af data, eller lignende. Her kan du lave en ekstra script-fil, f.eks. plot_data.m, som du kan køre fra dit shellscript (hvis f.eks. dit shellscript forbereder en fil med data der skal plottes). Alternativt kan du skrive dit plotting-script direkte i dit Bash shellscript med et Heredoc.

Et heredoc giver dig mulighed for at sende flere linier med kommandoer til et program. Her er et eksempel:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
echo "Hej fra Bash!"
python <<HEREDOC
print("Hej fra Python!")
print("Hej igen fra Python!")
HEREDOC
echo "Hej igen fra Bash!"
claus@ClausDesktop:~/work$ ./shellscript.sh
Hej fra Bash!
Hej fra Python!
Hej igen fra Python!
Hej igen fra Bash!

Du laver altså et Heredoc ved at skrive <<NAVN, og afslutter det med at skrive NAVN på en linie for sig selv. I eksemplet ovenfor blev HEREDOC brugt som navn, men mange bruger f.eks. EOF eller END i stedet - du kan også vælge et helt andet navn.

En vigtig fordel ved at bruge et Heredoc i stedet for et seperat script er, at du kan bruge variabler fra dit shellscript, se f.eks. dette eksempel:

claus@ClausDesktop:~/work$ cat shellscript.sh
#!/bin/bash
TAL="5.2"
DYR="Hund"
python <<HEREDOC
a = 2.2 + $TAL
print("Dyret er: $DYR")
print("Tallet plus 2.2 er: " + str(a))
print("Foerste parameter er: $1")
HEREDOC
claus@ClausDesktop:~/work$ ./shellscript.sh Hello World!
Dyret er: Hund
Tallet plus 2.2 er: 7.4
Foerste parameter er: Hello

Du har nu set de vigtigste elementer i Bash shellscripts.