Hyppää sisältöön

Docs CSC now features an automatic Finnish translation. Click here for more information.

Warning!

Puhti and Mahti are being decommissioned in stages, and their storage areas will become fully unavailable from 15 October 2026. Clean up unnecessary files and move any data you need to keep by 31 August 2026. See the Roihu data migration guide for instructions on transferring your data to Roihu.

Puhti scratch is very full: keep only active data there and move or delete everything else. No new Puhti scratch quota will be granted.

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 voi kirjoittaa yksinkertaisia ohjelmia, jotka säästävät paljon aikaa.

Skriptitiedoston laatiminen

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

nano my_test.script

Skriptitiedosto alkaa yleensä rivillä, joka määrittää käytettävän komentotulkin. Tässä oppaassa käytämme bash-tulkkia, 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
# This is a comment line that is not executed

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 skriptitiedostolle 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. Muuttujia voidaan asettaa syntaksilla:

variable=value

Huomaa, että yhtäsuuruusmerkin ympärillä ei ole välilyöntejä. Muuttujan arvo haetaan 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:

Operator Function
+ addition
- subtraction
* multiplication
/ division
% division remainder
** exponentiation

Yksinkertaisia kokonaislukuaritmetiikan esimerkkejä:

$ a=5
$ b=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 remainder $e"
5 divided by 3 results 1 and remainder 2

Bashissa voi käyttää myös yksiulotteisia taulukkomuuttujia eli muuttujia, jotka sisältävät luettelon alkioita. Määritetty taulukon alkio voidaan hakea käyttämällä indeksinumeroa sulkeissa taulukkomuuttujan nimen kanssa (${variable[index]}). Esimerkiksi voimme määrittää yksinkertaisen kolmen alkion taulukon komennolla:

array=(a b c)

Voimme nyt hakea joko koko taulukon tai vain yhden alkion siitä 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. Tässä tapauksessa esimerkiksi komento

echo ${#array[*]}

tulostaa arvon

3

Skriptin komentoriviargumentteihin viitataan sijaintiparametrien kautta. Parametri $0 viittaa skriptin nimeen, $1 ensimmäiseen argumenttiin, $2 toiseen argumenttiin 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, komennolle on annettava kaksi argumenttia. Ensimmäistä argumenttia käytetään tässä tapauksessa määrittämään kopiointikomennon lähdehakemisto, kun taas toinen argumentti on kohdehakemisto. Esimerkiksi komento

./my_script2.sh 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 ja suoritettavien komentojen määrittämiseen. Seuraavia lainausmerkkejä voidaan käyttää:

  • "" Käsittele lainausmerkkien sisällä oleva teksti kirjaimellisesti muuttujien korvaamisen jälkeen
  • '' 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 kohdalla

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

variable=sample1
echo "value = $variable"

tulos on

value = sample1

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

echo 'value = $variable'

saat tulosteeksi

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ä haluaisimme käyttää komentoa grep 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 suoritat 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ää komennon command2 tuotosta 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ä. Skripteissä niitä kuitenkin käytetään usein samankaltaisten komentojen suorittamiseen useita kertoja ja suoritettavien komentojen ohjaamiseen. 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 iteratiivisesti siten, että jokaisella kierroksella silmukkamuuttuja asetetaan yhtä suureksi kuin jokin 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ä niiden päätteeksi tulee .old, voisimme suorittaa komennon mv yhdeksän kertaa tai 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 käyttämällä jokerimerkkikuviota::

for filename in project_3/*.txt
do
   echo "Moving file: $filename"
   mv "$filename" "$filename.old"
done

Bashissa voit myös luoda for-silmukan, jossa numeerista indeksimuuttujaa kasvatetaan automaattisesti tietyllä askelkoolla jokaisella kierroksella. Tässä tapauksessa syntaksi on:

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

Alla on for-silmukka, joka suorittaa saman uudelleennimeämistoiminnon kuin yllä, mutta käyttää alkioina vain 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 suorittamista 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 operandeja komentojen if ja while ehtolauseissa. Huomaa, että bash käyttää eri ehtolauseita merkkijonoille ja kokonaisluvuille. Esimerkiksi merkkijonojen yhtäsuuruus testataan operaattorilla ==, kun taas kokonaislukujen yhtäsuuruus testataan operaattorilla -eq. Syntaksi on myös tarkka sulkeiden välisten välilyöntien suhteen, eikä ehtolauseen määritelmä [[a == b]] toimi, vaan se pitää korjata muotoon [[ a == b ]].

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

Statement Operation
[[ a == b ]] True if strings a and b are equal
[[ a != b ]] True if strings a and b are not equal
[[ a =~ b ]] True if strings a and b are similar (allows wildcards)
[[ a < b ]] True if string a is alphabetically before string b
[[ a > b ]] True if string a is alphabetically after string b
[[ a -eq b ]] True if integers a and b are equal
[[ a -ne b ]] True if integers a and b are not equal
[[ a -lt b ]] True if integer a is less than b
[[ a -gt b ]] True if integer a is greater than b
[[ a -le b ]] True if integer a is less or equal to b
[[ a -ge b ]] True if integer a is greater or equal to b
[[ -e name ]] True if file exists
[[ -n a ]] True if string a has non-zero length
[[ A || B ]] True if condition A or condition B is true (logical OR)
[[ A && B ]] True if condition A and condition B is true (logical AND)
[[ ! A ]] True if condition A is not true

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ä sisältäviä muuttujia (merkkijonoja):

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 pienempi kuin ja suurempi kuin -vertailuja, 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. Numeerinen vertailu

[[ 123 -gt 3 ]]

on kuitenkin TRUE.

On olemassa useita operaattoreita, joilla voidaan testata tiedoston eri ominaisuuksia. Yleisimmin käytetty operaattori on -e, joka tarkistaa, onko tiedosto olemassa. Oletetaan esimerkkinä, 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 tiedoston olemassaolon testaamiseen:

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 komentoa echo 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, kannattaa käyttää komentoa printf komennon echo sijaan. Komennon printf syntaksi on:

printf "format definition" arguments_to_print

format definition määrittää, millaista tulostetta tulostetaan. Yleisiä tyyppejä ovat teksti (%s), kokonaisluvut (%i) ja liukuluvut (%f). Muotomäärittelyt voivat myös määrittää, kuinka paljon tilaa kullekin argumentille varataan ja miten se sijoitetaan sarakkeeseen. Alla on joitakin yksinkertaisia esimerkkejä komennon printf käytön havainnollistamiseksi:

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

tulostaa

1 Hello World 23.750000

Tässä muotomäärittely määrittää, että ensimmäinen argumentti käsitellään kokonaislukuna, toinen ja kolmas merkkijonoina ja neljäs argumentti liukulukuna. Huomaa, että oletusarvoisesti printf ei lisää rivinvaihtomerkkiä tulosteen loppuun. Tätä varten muotomäärittelyn loppuun tulee lisätä määrittely \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 sen jälkeen kummallekin merkkijonolle kymmenen merkkiä. Liukuluku esitetään kuudella merkillä, joista kaksi on desimaalipisteen jälkeen.

Voit myös lisätä muotomää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