A Bash nyelvben is megvannak a feltételes utasítások. Ezeket fogjuk megnézni az órán.
Bashben tömérdek feltételvizsgáló operátor van. Alább egy összefoglaló lista látható. A lista után meg fogjuk nézni, hogy az egyes operátorokat hogyan tudjuk használni.
Legyenek x és y egész számok.
x=10; y=20
| Művelet | Bash | Jelentés |
| ———– | ———– | - |
| x < y | $x -lt $y| less-than|
| x <= y | $x -le $y | less-or-equal |
| x > y | $x -gt $y | greater-than |
| x >= y | $x -ge $y | greater-or-equal|
| x == y | $x -eq $y | equal |
| x != y | $x -ne $y | not-equal|
Legyenek text1 és text2 stringek.
text1=blabla
text2="Bla bla"
| Bash | Jelentés |
|---|---|
$text1 == $text2 |
egyenlőség |
$text1 != $text2 |
nem egyenlőség |
$text1 < $text2 |
text1 rendezéskor text2 elé kerül (jelenlegi locale szerint) |
$text1 > $text2 |
text1 rendezéskor text2 után kerül (jelenlegi locale szerint) |
-n $text1 |
text1 nem üres |
-z $text1 |
text1 üres |
$text1 =~ "regex_pattern" |
text1-re illeszkedik a regex_pattern reguláris kifejezés |
Legyen file_path egy fájl elérési útja, other_file_path pedig egy másik fájllé.
| Vizsgálat tárgya | Bash | Jelentés |
|---|---|---|
| A fájl létezik-e | -e file_path |
exists |
| Directory létezik-e | -d file_path |
directory |
| Legutóbbi olvasás óta módosult-e | -N file_path |
new |
| Jelenlegi user által birtokolt-e | -O file_path |
owned |
| Olvasható-e | -r file_path |
readable |
| Írható-e | -w file_path |
writeable |
| Futtatható-e | -x file_path |
executable |
| Nem üres-e | -s file_path |
size greater than zero byte |
| Újabb-e egyik fájl a másiknál | file_path -nt other_file_path |
newer than |
| Régebbi-e egyik fájl a másiknál | file_path -ot other_file_path |
older than |
| Ugyanaz a fájl-e (a fájlrendszeren) | file_path -ef other_file_path |
equal file |
Most már ismerjük a vizsgáló operátorokat. Nézzük, milyen szintaxissal tudjuk ténylegesen használni őket!
Ezt már láttuk korábban. Továbbra is ugyanazt csinálják, mint eddig, csak a test parancs
segítségével feltételvizsgálatot társíthatunk hozzájuk. Például:
x=10; y=20
test $x -lt $y && echo "Smaller" # -lt means less-than
test $x -gt $y && echo "Greater" # -gt means greater-than
Ez a fajta feltételmegadás egysorosok esetén használható jól. Például a következő kód létrehoz egy fájlt, ha az nem létezik.
test -e some_file.txt || touch some_file.txt # -e means exists
Létezik két segédprogram is a rendszeren, a true és a false. Ezek a nevükhöz illően
úgy viselkednek, mintha egy mindig igazra (true) vagy egy mindig hamisra (false) kiértékelődő
feltételt írtunk volna a helyükre. Például:
true && echo "This is written"
true || echo "This is NOT written"
false && echo "This is NOT written"
false || echo "This is written"
A test parancsnak van egy shortcut-ja is, szögletes zárójelek formájában. A következő két
sor parancs ugyanaz.
test 1 -ne 2 && echo "Thing"
[ 1 -ne 2 ] && echo "Same thing"
Ez az if szerkezetnél lesz intuitívabb. Szintén az if szerkezetnél fogjuk használni a
[[ feltétel ]], tehát a kétszeres szögletes zárójeles szerkezetet is.
[[ 1 -ne 2 ]] && echo "Almost the same thing"
Ez majdnem ugyanolyan, mint az egyszeres szögletes zárójel (és ennélfogva a test parancs),
kivéve, hogy ez többféle szintaxist megenged. Például csak ebben tudjuk használni a
reguláris kifejezés összehasonlító operátort =~.
A továbbiakban leginkább az if-else szerkezetet használjuk, mert ez rugalmasabb.
Az if szerkezetek írásakor ugyanúgy használhatjuk a test operátort, mint eddig:
if test 1 -eq 1; then
echo "true"
fi
A következőt azonban (főleg imperatív nyelvek ismeretében) intuitívabb olvasni:
if [ 1 -eq 1 ]; then
echo "true"
fi
A két fönti kifejezés ugyanazt jelenti. A továbbiakban mi többnyire a lenti kifejezést használjuk majd, mert gazdagabb feltételmegfogalmazást biztosít nekünk:
if [[ 1 -eq 1 ]]; then
echo "true"
fi
Ennél az egyszerű példánál természetesen mindhárom ugyanazt jelenti.
Az if szerkezet sajátossága Bashben a többi nyelvhez képest, hogy a lezáró parancsa a fi,
ami az if fordítva. Ezt a logikát máshol is látjuk majd.
Az if szerkezethez rakhatunk else ágat is:
if [[ 1 -ne 1 ]]; then
echo "true"
else
echo "false"
fi
Az else-if szerkezetet a Bash a következőképpen oldja meg:
x=10
if [[ $x -eq 1 ]]; then
echo "equal"
elif [[ $x -lt 1 ]]; then
echo "less than"
else
echo "greater than"
fi
Látható, hogy az else-if szavak összevonódtak az elif szóba.
A C-ből ismert switch-case szerkezet is megtalálható itt, a következő szintaxissal:
read -p "Which type of person are you, 0, 1, 2 or 3" ptype
case $ptype in
0)
echo "You are chaotic evil.";;
1)
echo "You are a mermaid.";;
2)
echo "You are phylosopher.";;
3)
echo "You are a Tauren Druid.";;
*)
echo "That's not a person type, it's a default!";;
esac
A [[]] szerkezetben hasonlóan tudjuk használni az ÉS, VAGY, NEM logikai kifejezéseket, ahogy C-ben.
Például a következő ellenőrzi, hogy x 10 és 20 között legyen:
if [[ $x -gt 10 && $x -lt 20 ]]; then
echo "OK"
fi
Vagy hogy NE legyen szám:
reg_ex='^[0-9]+$'
# A felkiáltójel a következő kifejezés tagadása
if [[ ! $x =~ $reg_ex ]]; then
echo "Not a number."
fi
Most már nagyjából mindent tudunk, amire a feltételes kódjainkhoz szükség lesz. Lássunk példákat!
A következő példákban gyakoriak lesznek az input ellenőrzések. Amennyiben egy input nem megfelelő,
hibával lépünk ki a programból. A hiba jelzésére a program visszatérési értéke alkalmas. Ha a
visszatérési érték nem nulla, az hibát jelent. Idő előtt kilépni az exit paranccsal tudunk.
Például:
#!/bin/bash
if [ 1 -ne 1 ]; # very unexpected error
exit 1
fi
Ha nem írunk számot az exit után (vagy nem írunk exit parancsot a programba), akkor a visszatérési
érték automatikusan nulla lesz (még akkor is, ha valamelyik parancs hibát adott a programon belül).
Például a következő program nem tér vissza hibával, bár a grep maga hibára fut.
#!/bin/bash
grep "hello" nonexistent_file_blabla.txt
Készítsünk egy shell scriptet, ami bemenetként egy téglalap két oldalának hosszát várja, és kiírja a síkidom területét! Valósítsuk meg csak egész számokkal! (Természetesen végezzünk ellenőrzéseket az inputon!)
#!/bin/bash
if [[ $# -ne 2 ]]; then
echo "Two inputs are needed, $# is given." 1>&2
exit 1
fi
reg_ex='^[0-9]+$'
if [[ ! $1 =~ $reg_ex || ! $2 =~ $reg_ex ]]; then
echo "Both inputs must be positive integer numbers!" 1>&2
exit 1
fi
echo "Area is: $(($1 * $2))"
A 1>&2 kifejezés az echo után átirányítja a standard output streamet a standard error streamre.
Az első if kifejezés ellenőrzi, hogy pontosan két inputot adtunk-e meg.
A második if kifejezés biztosítja, hogy ha az első vagy a második kifejezés nem felel meg az egész
számokra vonatkozó reguláris kifejezésünknek, akkor hibaüzenettel lépjünk ki.
Legyen adott egy fájl name_id_pairs.txt néven, ami id és név párosokat tartalmaz.
Készítsünk egy shell scriptet, ami bekéri a nevet, és kiírja a hozzá tartozó id-t,
vagy hibát ad, ha a név nem található a fájlban.
A fájl tartalma legyen a következő:
zsuzso:aef7421b
alfonz:eef4555b
barnabas:98726371
tom:aaaabbbb
jerry:cccdddee
hasselhoff:cafebabe
A következő script megvalósítja a feladatot:
#!/bin/bash
read -p "Please, type the name you are searching for! " name
row=$(cat name_id_pairs.txt | grep $name)
if [[ -z $row ]]; then
echo "No name like that." 1>&2
exit 1
fi
id=$(echo $row | cut -d ':' -f 2)
echo $id
Az MVK Zrt. elérhetővé tesz egy szabványos GTFS adatbázist a fejlesztők számára, hogy a menetrendi adatokat a saját applikációikba tudják integrálni. Írjunk egy shell script fájlt, amely letölti ezt az adatbázist, és kilistázza belőle azokat az utakat, amelyek a Centrumból indulnak, vagy a Centrumba mennek!
Parancsok: wget, unzip (kitömörítésre), cat, grep
Szükséges ellenőrzések:
Megjegyzés: A tanszéki gépekről certificate problémák miatt a korábbiakban nem volt letölthető az adatbázis, ezért egy github repository-ba is feltettem. A hozzá tartozó parancs ki van kommentezve. Hibaüzenet esetén cseréljük le a jelenlegi url-t a következőre: https://raw.githubusercontent.com/bbalage/BashExamples/master/assets/gtfs.zip
#!/bin/bash
if [ -e gtfs.zip ]; then
rm gtfs.zip
fi
if [ -d gtfs ]; then
rm -r gtfs
fi
wget "https://gtfsapi.mvkzrt.hu/gtfs/gtfs.zip"
unzip gtfs.zip -d gtfs # próbáljuk ki -d kapcsoló nélkül is...
cat gtfs/routes.txt | grep "Centrum"
Készítsünk egy shell scriptet, ami bekéri a felhasználó születési dátumát yyyy.mm.dd formátumban!
Ellenőrizzük le a dátum helyességét, és írjuk ki, hogy a felhasználó hány éves!
Használjuk a date parancsot a jelenlegi dátum lekérésére!
#!/bin/bash
read -p "Please, type your birthdate in yyyy.mm.dd format! " bdate
reg_ex='^[0-9]{4}\.[0-9]{2}\.[0-9]{2}$'
if [[ ! $bdate =~ $reg_ex ]]; then
echo "Date is not in proper format!" 1>&2
exit 1
fi
byear=$(echo $bdate | cut -f 1 -d '.')
bmonth=$(echo $bdate | cut -f 2 -d '.')
bday=$(echo $bdate | cut -f 3 -d '.')
# A date parancs le tudja ellenőrizni egy szintaktikailag helyes
# dátum validitását. Például a hónap nem-e nagyobb 12-nél, stb.
date -d "$byear-$bmonth-$bday" > /dev/null || exit 1
bseconds=$(date -d "$byear-$bmonth-$bday" +%s)
cseconds=$(date +%s)
age_in_seconds=$((cseconds - bseconds))
if [[ $age_in_seconds -lt 0 ]]; then
echo "You haven't been born yet!" 1>&2
exit 1
fi
echo $(( age_in_seconds / 60 / 60 / 24 / 365 ))
Valósítsuk meg az 1. példa feladatát, de ezúttal lebegőpontos számokkal!
Valósítsuk meg a 2. példa feladatát, de ezúttal ne csak name_id_pairs.txt nevű fájlra
működjön, hanem bármilyen nevű fájlra! A fájl nevét a script bemeneti paraméterként
fogadja! Ellenőrizzük, hogy a fájl létezik és olvasható-e, mielőtt a funkciók további részét
megvalósítjuk!
Valósítsuk meg a 3. példa feladatát, de ezúttal a Centrum helyett bármelyik végállomást fogadjuk el, és bemeneti paraméterként adjuk át azt a scriptnek. Ha nincs ilyen végállomás, írjunk hibaüzenetet!