Hyppää sisältöön

Welcome to our weekly research support coffee hour on Zoom! Click here for more information.

Warning!

Puhti scratch disk is becoming very full (80+ % ) resulting in performance degradation. Everybody is advised to only keep actively processed data on scratch, all other data should be deleted, transferred to host institute or stored in Lumi-O. No new quota will be granted. Click here for a tool for examining your disk usage.

Linuxin bash-skriptit

Yksi tapa hyödyntää Linuxin joustavuutta on käyttää komentokomentosarjoja. Komentosarja on yksinkertaisesti tiedosto, joka sisältää joukon tavallisia Linux- komentoja, jotka komentotulkki suorittaa automaattisesti annetussa järjestyksessä. Verrattuna varsinaisiin ohjelmointikieliin, kuten Pythoniin, Perliin tai C:hen, ohjelmointi Linuxin komentotulkeilla (bash, tcsh, csh tai sh) on laskennallisesti melko tehotonta. Käytännöllisiä Linux-skriptejä voidaan kuitenkin usein rakentaa muutamassa minuutissa. Komentosarjoista ei tarvitse tietää kovin paljon, jotta pystyy kirjoittamaan yksinkertaisia ohjelmia, jotka säästävät paljon aikaa.

Skriptitiedoston laatiminen

Skriptitiedosto on yksinkertainen tekstitiedosto, joka voidaan tehdä tavallisilla tekstieditoreilla, kuten nano, Emacs tai vi. Luo uusi skriptitiedosto kirjoittamalla esimerkiksi:

nano my_test.script

Skriptitiedosto alkaa yleensä rivillä, joka määrittää käytettävän komentotulkin. Tässä ohjeessa käytämme bash-komentotulkkia, joka on CSC:n oletuskomentotulkki. Bashin määrittävä rivi on:

#!/bin/bash

Sen jälkeen lisäät Linux-komennot, jotka haluat suorittaa. Käytännössä kirjoitat tiedostoon ne komennot, joita normaalisti käyttäisit tehtävän suorittamiseen interaktiivisessa komentotulkissa. Esimerkiksi seuraavaa skriptiä voidaan käyttää alihakemiston mapfiles luomiseen ja kaikkien .map-tiedostojen kopioimiseen sinne:

#!/bin/bash
mkdir mapfiles
cp *.map mapfiles/

Jos skriptin rivi alkaa merkillä #, se ohitetaan, ja loppuriviä pidetään kommenttina (paitsi ensimmäistä riviä, joka alkaa merkeillä #!).

#!/bin/bash
# Tämä on kommenttiriviä, jota ei suoriteta

mkdir mapfiles
cp *.map mapfiles/

Kun olet tallentanut skriptitiedoston ja sulkenut editorin, voit suorittaa skriptitiedoston komennot antamalla komennon:

source my_test.script

Vaihtoehtoisesti voit antaa skriptitiedostollesi suoritusoikeudet komennolla

chmod u+x my_test.script

ja suorittaa skriptin sitten komennolla:

./my_test.script

Muuttujat ja taulukot

Skripteissä voi käyttää muuttujia, silmukoita ja ehtolauseita. Muuttujat voidaan asettaa syntaksilla:

variable=value

Huomaa, että yhtäsuuruusmerkin ympärillä ei ole välilyöntejä. Muuttujan arvoon viitataan merkillä $,

$variable

tai

${variable}

Esimerkiksi komento

echo $variable

kirjoittaa muuttujan arvon tulosteeseen. Huomaa, että bash-skripteissä muuttujia käsitellään joko merkkijonoina (eli tekstinä) tai kokonaislukuina. Tämä tarkoittaa, että desimaalilukuja ei voi käyttää bash- skripteissä matemaattisiin operaatioihin.

Esimerkki merkkijonomuuttujien käytöstä:

$ name=Veikko
$ familyname=Salo
$ address="CSC Espoo"
$ echo "Person: ${name} ${familyname} works at ${address}."

Person: Veikko Salo works at CSC Espoo.

Kokonaislukumuuttujille voi tehdä yksinkertaista aritmetiikkaa syntaksilla ((expression)). Yleisesti käytetyt aritmeettiset operaatiot on lueteltu alla olevassa taulukossa:

Operaattori Toiminto
+ yhteenlasku
- vähennyslasku
* kertolasku
/ jakolasku
% jakojäännös
** potenssiin korotus

Yksinkertaisia kokonaislukuaritmetiikan esimerkkejä:

$ a=5
$ c=3
$ ((c = a + b))
$ echo  $a plus $b is equal to $c
5 plus 3 is equal to 8
$ ((d = a / b))
$ ((e = a % b))
$ echo "$a divided by $b results $d and reminder $e"
5 divided by 3 results 1 and reminder 2

Bashissa voidaan käyttää myös yksiulotteisia taulukkomuuttujia eli muuttujia, jotka sisältävät luettelon alkioita. Tiettyyn taulukon alkioon voidaan viitata käyttämällä indeksinumeroa hakasulkeissa taulukkomuuttujan nimen yhteydessä (${variable[index]}). Esimerkiksi voimme määrittää yksinkertaisen kolmen alkion taulukon komennolla:

array=(a b c)

Voimme nyt tulostaa joko koko taulukon tai vain yhden sen alkioista komennolla

echo ${array[*]} 

Tämä tulostaa

a b c

kun taas komento

echo ${array[2]}

tulostaa

c

Huomaa, että taulukossa indeksointi alkaa nollasta, joten yllä oleva esimerkkikomento tulostaa taulukon kolmannen alkion. Voit tarkistaa taulukon alkioiden määrän lisäämällä merkin # muuttujan nimen alkuun. Esimerkiksi tässä tapauksessa komento

echo ${#array[*]}

tulostaa arvon

3

Taulukkomuuttujien erikoistapaus on $, joka sisältää komentoriviargumentit, eli alkiot, jotka voit antaa skriptillesi syöteparametreina. Tässä tapauksessa taulukon $0 arvo viittaa varsinaisen skriptin nimeen, $1 ensimmäiseen argumenttiin, $2 toiseen ja niin edelleen. $# viittaa argumenttien määrään ja $@ koko argumenttiluetteloon. Alla on esimerkkiskripti, joka havainnollistaa $-taulukkomuuttujan käyttöä:

#!/bin/bash
from_dir=$1
to_dir=$2
mkdir $to_dir
cp $from_dir/*.map $to_dir

Jos suoritamme nyt tämän skriptin, jonka nimi on esimerkiksi my_script2.sh, meidän on annettava komennolle kaksi argumenttia. Ensimmäistä argumenttia käytetään tässä tapauksessa kopiointikomennon lähdehakemiston määrittämiseen, kun taas toinen argumentti on kohdehakemisto. Esimerkiksi komento

./my_script2.csh source_data map_files

kopioisi kaikki tiedostot, joiden pääte on .map, hakemistosta nimeltä source_data uuteen hakemistoon nimeltä map_files.

Lainausmerkit

Bashissa käytetään kolmea erilaista lainausmerkkiä. Lainausmerkkejä tarvitaan usein muuttujien määrittämiseen ja suoritettavien komentojen kirjoittamiseen. Seuraavia lainausmerkkejä voidaan käyttää:

  • "" Käsittele lainausmerkkien sisällä oleva teksti kirjaimellisesti sen jälkeen, kun mahdolliset muuttujat on korvattu arvoillaan
  • '' Käsittele lainausmerkkien sisällä oleva teksti kirjaimellisesti
  • `` Käsittele lainausmerkkien sisällä oleva teksti komentona, suorita komento ja korvaa se sitten komennon tulosteella lainausmerkkien kohdassa

Alla on joitakin esimerkkejä havainnollistamaan eri lainausmerkkien toiminnallisia eroja. Lainausmerkkejä voidaan käyttää muuttujien ja argumenttien kanssa. Kun käytetään kaksin- tai yksinkertaisia lainausmerkkejä, kaikki lainausmerkkien sisällä oleva teksti käsitellään yhtenä argumenttina. Näiden kahden lainausmerkkityypin ero on se, että kun käytetään kaksinkertaisia lainausmerkkejä, muuttujat korvataan niiden arvoilla, kun taas yksinkertaisilla lainausmerkeillä kaikki teksti käytetään sellaisenaan. Jos ajat komennot

variable=sample1
echo "value = $variable"

tulos on

value = sample1

Mutta jos käytät sen sijaan yksinkertaisia lainausmerkkejä

echo 'value = $variable'

saat tulosteen

value = $variable

Linux-komennoissa ja -skripteissä lainausmerkkejä käytetään tyypillisesti määrittämään argumentteja, jotka sisältävät välilyöntejä tai muita erikoismerkkejä. Oletetaan, että haluamme käyttää grep-komentoa poimimaan kaikki rivit, jotka sisältävät merkkijonon file size, tiedostosta nimeltä files.txt. Seuraava komento ei toimisi:

grep file size files.txt

Jos ajat yllä olevan komennon, saat virheilmoituksen, koska sana size tulkitaan nyt toiseksi argumentiksi, joka määrittää syötetiedoston. Voimme korjata tilanteen käyttämällä lainausmerkkejä:

grep "file size" files.txt

Nyt ensimmäinen argumentti, joka määrittää haettavan merkkijonon, on file size (mukaan lukien sanojen välinen välilyönti), ja toinen argumentti, joka määrittää syötetiedoston, on nyt files.txt kuten alun perin oli tarkoitus.

Kolmannella lainausmerkkityypillä `` on erityinen merkitys. Näillä lainausmerkeillä voit saada yhden Linux-komennon tuottamaan argumentin toiselle Linux-komennolle. Perussyntaksi `` on:

command1 `command2`

missä command1 käyttää command2:n tuottamaa tulosta argumenttina. Bash- skriptissä sama toiminnallisuus voidaan tehdä myös syntaksilla

$(command)

Silmukat ja ehtolauseet

Silmukoita ja ehtolauseita käytetään harvoin interaktiivisessa komentorivikäytössä. Niitä käytetään kuitenkin usein skripteissä suorittamaan samankaltaisia komentoja useita kertoja ja ohjaamaan suoritettavia komentoja. Bash tarjoaa laajan valikoiman silmukoita, ehtolauseita ja muita ohjausrakenteita. Tässä osiossa näytämme esimerkkejä joistakin yleisimmin käytetyistä ohjausrakenteista.

for-silmukka suorittaa määritetyt komennot toistuvasti siten, että jokaisella iteraatiolla silmukkamuuttuja asetetaan yhtä suureksi kuin yksi annetun alkioluettelon alkioista. Bashissa for-silmukka tehdään komentorakenteella:

for variable in element_list
do
   commands
done

Esimerkiksi silmukka

for filename in sample1.txt sample2.txt sample3.txt
do
   echo ${filename}
done

tulostaisi

sample1.txt
sample2.txt
sample3.txt

Tyypillisesti argumenttiluettelo sisältää käsiteltäviä tiedostonimiä, mutta se voi sisältää myös mitä tahansa muita parametreja. Oletetaan esimerkiksi, että meillä on hakemisto nimeltä project_3, joka sisältää yhdeksän tiedostoa nimeltä sample1.txt, sample2.txt, ..., sample9.txt. Hakemiston sisällön näkemiseen voimme käyttää komentoa ls:

$ ls project_3/
sample1.txt sample3.txt sample5.txt sample7.txt sample9.txt  
sample2.txt sample4.txt sample6.txt sample8.txt  

Jos haluaisimme nimetä jokaisen näistä tiedostoista uudelleen niin, että niillä on pääte .old, voisimme ajaa komennon mv yhdeksän kertaa, tai voisimme käyttää for-silmukkaa:

for filename in sample1.txt sample2.txt sample3.txt sample4.txt \
sample5.txt sample6.txt sample7.txt sample8.txt sample9.txt
do
   echo "Renaming file: ${filename}"
   mv project_3/${filename} project_3/${filename}.old
done

Yllä oleva for-silmukka on silti melko kömpelö, koska meidän täytyy kirjoittaa kaikki tiedostonimet alkioluetteloon. Voimme välttää tämän korvaamalla alkioluettelon ilmaisulla $(ls project_3/). Nyt komentoa ls project3 käytetään tuottamaan luettelo käsiteltävistä tiedostonimistä:

for filename in $(ls project_3/)
do
   echo "Moving file: $filename"
   mv project_3/$filename project_3/"$filename".old
done

Bashissa voit myös luoda for-silmukan, jossa numeerisen indeksimuuttujan arvoa kasvatetaan automaattisesti tietyllä askelkoolla jokaisella iteraatiolla. Tässä tapauksessa syntaksi on:

for ((variable=start; variable<=end; i++)) 

Alla on for-silmukka, joka suorittaa saman uudelleennimeämistoiminnon kuin yllä, mutta käyttää alkioina pelkkiä numeroita.

for ((number=1; number<=9; number++))
do
   echo "Moving file: sample${number}.txt"
   mv project_3/sample${number}.txt project_3/sample${number}.txt.old
done

while-silmukassa silmukka jatkaa toimintaansa niin kauan kuin määritetty ehtolause on tosi. Bashissa while-silmukka voidaan tehdä syntaksilla:

while [[ condition ]]
do
   commands
done

Yllä for-silmukalla tehty uudelleennimeämistoiminto voitaisiin tehdä myös while-silmukalla:

number=1
while [[ $number -le 9 ]]
do
   echo "Moving file: sample${number}.txt"
   mv project_3/sample${number}.txt project_3/sample${number}.txt.old
   ((number = number + 1))
done

Yllä olevassa esimerkissä muuttuja nimeltä number asetetaan ensin arvoon 1. Tämän muuttujan arvoa kasvatetaan sitten yhdellä jokaisen iteraatiokierroksen lopussa. Iteraatioita jatketaan, kunnes muuttuja saavuttaa arvon 10.

Ehtolauseet (if) voidaan tehdä seuraavasti:

if [[ condition ]]
then
   commands
else
   commands
fi

Voit käyttää alla olevassa taulukossa lueteltuja operaattoreita if- ja while- komentojen ehtolauseissa. Huomaa, että bash käyttää eri ehtolauseita merkkijonoille ja kokonaisluvuille. Esimerkiksi merkkijonojen yhtäsuuruutta testataan operaattorilla ==, kun taas kokonaislukujen yhtäsuuruutta testataan operaattorilla -eq. Syntaksi on myös tarkka hakasulkeiden välisten välilyöntien suhteen, eikä ehtolauseen määrittely [[a == b]] toimi, vaan se pitää korjata muotoon [[ a == b ]].

Yleisesti käytetyt merkkijono-, kokonaisluku- ja tiedosto-operaattorit if- ja while- lauseissa on lueteltu alla.

Lauseke Toiminto
[[ a == b ]] Tosi, jos merkkijonot a ja b ovat samat
[[ a != b ]] Tosi, jos merkkijonot a ja b eivät ole samat
[[ a =~ b ]] Tosi, jos merkkijonot a ja b ovat samankaltaiset (sallii jokerimerkit)
[[ a < b ]] Tosi, jos merkkijono a on aakkosjärjestyksessä ennen merkkijonoa b
[[ a > b ]] Tosi, jos merkkijono a on aakkosjärjestyksessä merkkijonon b jälkeen
[[ a -eq b ]] Tosi, jos kokonaisluvut a ja b ovat samat
[[ a -ne b ]] Tosi, jos kokonaisluvut a ja b eivät ole samat
[[ a -lt b ]] Tosi, jos kokonaisluku a on pienempi kuin b
[[ a -gt b ]] Tosi, jos kokonaisluku a on suurempi kuin b
[[ a -le b ]] Tosi, jos kokonaisluku a on pienempi tai yhtä suuri kuin b
[[ a -ge b ]] Tosi, jos kokonaisluku a on suurempi tai yhtä suuri kuin b
[[ -e name ]] Tosi, jos tiedosto on olemassa
[[ -n a ]] Tosi, jos merkkijonon a pituus on suurempi kuin nolla
[[ A || B ]] Tosi, jos ehto A tai ehto B on tosi (looginen OR)
[[ A && B ]] Tosi, jos ehto A ja ehto B ovat tosia (looginen AND)
[[ ! A ]] Tosi, jos ehto A ei ole tosi

Alla on joitakin esimerkkejä if-komentorakenteista.

Tarkista, onko kokonaislukumuuttuja x suurempi kuin 10:

if [[ $x -gt 10 ]]
then
   echo "The value of variable x is more than 10"
fi

Tarkista, onko muuttuja x suurempi kuin 10 mutta pienempi kuin 20:

if [[ $x -gt 10 && $x -lt 20 ]]
then
   echo "The value of variable x is more than 10 but less than 20"
else
   echo "The value of x is out of range"
fi

Voit vertailla myös tekstiä (merkkijonoja) sisältäviä muuttujia:

if [[ $answer == "yes" ]]
then
   echo "Your answer was: yes"
elif [[ $answer == "no" ]]
then
   echo "Your answer was no"
else
   echo "You didn't answer yes or no"
fi

Kun käytät vertailuja pienempi kuin ja suurempi kuin, sinun tulee olla varovainen, ettet sekoita merkkijono- ja kokonaislukuvertailuja. Esimerkiksi seuraava ehto

[[ 123 > 3 ]]

on FALSE, koska merkkijono 123 on aakkosjärjestyksessä ennen merkkijonoa 3. Sen sijaan numeerinen vertailu

[[ 123 -gt 3 ]]

on TRUE.

On olemassa useita operaattoreita, joilla voit testata tiedoston eri ominaisuuksia. Yleisimmin käytetty operaattori on -e, joka tarkistaa, onko tiedosto olemassa. Oletetaan esimerkiksi, että meillä on yksinkertainen tiedostonimiluettelo nimeltä checklist.txt. Haluaisimme tarkistaa, mitkä näistä tiedostoista löytyvät nykyisestä hakemistosta. Voimme käyttää for-silmukkaa kaikkien tiedostonimien tutkimiseen ja if-komentoa yhdessä -e-ehdon kanssa testaamaan, onko tiedosto olemassa:

for file_name in $(cat checklist.txt)
do
   if [[ -e $file_name ]]
   then
      echo "File $file_name was found"
   else
      echo "File $file_name was not found"
   fi
done

Tulosteen kirjoittaminen

Edellisissä esimerkeissä olemme jo käyttäneet echo-komentoa tekstin ja muuttujien kirjoittamiseen vakiotulosteeseen (eli näytölle tai tiedostoon vakiotulosteen uudelleenohjauksella). Esimerkiksi komento

echo "Hello world"

tulostaa

Hello world

echo-komentoa voidaan käyttää tulostamiseen monissa tapauksissa, mutta se ei tarjoa hyviä työkaluja hyvin muotoillun tulosteen tekemiseen määritellyillä sarakkeilla. Tilanteissa, joissa tarvitaan hyvin jäsenneltyä tekstimuotoista tulostetta, tulisi käyttää printf-komentoa echo:n sijaan. printf-komennon syntaksi on:

printf "format definition" arguments_to_print

format definition määrittää, minkä tyyppistä tulostetta kirjoitetaan. Yleisiä tyyppejä ovat teksti (%s), kokonaisluvut (%i) ja liukuluvut (%f). Muotoilumäärittelyt voivat myös määrittää, kuinka paljon tilaa kullekin argumentille varataan ja miten se sijoitetaan sarakkeeseen. Alla on joitakin yksinkertaisia esimerkkejä havainnollistamaan printf- komennon käyttöä:

printf "%i %s %s %f\n" 1 Hello World 23.75

tulostaa

1 Hello World 23.750000

Tässä muotoilumäärittely määrittää, että ensimmäistä argumenttia käsitellään kokonaislukuna, toista ja kolmatta merkkijonoina ja neljättä argumenttia liukulukuna. Huomaa, että oletusarvoisesti printf ei lisää rivinvaihtomerkkiä tulosteen loppuun. Jotta näin tapahtuisi, muotoilumäärittelyn tulee päättyä määrittelyyn \n.

Seuraavassa esimerkissä määritämme, kuinka monta merkkiä kullekin argumentille varataan. Komento

printf "%4i %10s %10s %6.2f\n" 1 Hello World 23.75

tulostaa

1 Hello World 23.75

Tässä varaamme ensimmäiselle kokonaisluvulle neljä merkkiä ja sitten kymmenen merkkiä kummallekin merkkijonolle. Liukuluku esitetään kuudella merkillä, joista kaksi on desimaalipisteen jälkeen.

Voit myös lisätä muotoilumäärittelyyn tekstiä ja ohjausmerkkejä, kuten sarkaimen (\t). Komento

printf "This is my %i:st %s %s\t %6.1f\n" 1 Hello World 23.75

tulostaa

This is my 1:st Hello World 23.8

Linux-skripteissä printf-komentoa käytetään tyypillisesti muuttujissa tallennettujen arvojen tulostamiseen. Esimerkiksi komennot

unit=3g
value=5.3
printf "The resulting value from:%4s\t is:\t%6.2f\n" $unit $value

tulostavat

The resulting value from: 3g is: 5.30

Suomenkielinen tekoälykäännös

Sisällössä voi esiintyä virheellistä tietoa tekoälykäännöksestä johtuen.

Klikkaa tästä antaaksesi palautetta