Tartalom
Bevezető
Programozásnál lényeges dolog, hogy ha megírunk egy bizonyos feladatot elvégző programot, akkor a kódunk legyen felkészítve többféle eshetőségre, variációra és lehetőség szerint paraméterezhető is legyen – amivel finomhangolhatóvá tehetjük a működését. Nincs ez másképpen a shell szkriptek esetében sem. A mai példában megnézzük, hogyan tudjuk rugalmasan kezelni szkriptjeinkben a kapott paramétereket, aminek segítségével mások számára is könnyebben használható programokat készíthetünk.
A program felépítése
Létrehozás
Hozzunk létre egy tetszőleges fájlt, pl.:
touch parameterek_rugalmas_feldolgozasa
Majd adjunk rá futtatási jogot:
chmod +x parameterek_rugalmas_feldolgozasa
Ezután jöhetnek a szerkesztgetések:
nano parameterek_rugalmas_feldolgozasa
És ha majd kerül bele tartalom, akkor időnként a program kipróbálása pl. egy másik terminálban:
./ nano parameterek_rugalmas_feldolgozasa
Kezdhetjük is.
Az alapok
Először állítsuk össze a programunk alapját, amelyben elhelyezzük a kapcsolóink számára szükséges asszociatív tömböt, és néhány egyéb alapbeállítást.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #!/bin/bash # ------------------------------------------------------------------------------ # Linuxportál példaprogram: Paraméterek rugalmas feldolgozása # ------------------------------------------------------------------------------ # ---------------------------------------------------------- # Kapcsolók/paraméterek alapértelmezett értékei # ---------------------------------------------------------- declare -A PARAMS PARAMS=( [DEBUG]="" # Debug mód [HELP]="" # súgó megjelenítése [VERSION]="" # Verzió információk [EGYEB1]="" # Egyéb paraméter #1 [EGYEB2]="" # Egyéb paraméter #2 [EGYEB3]="" # Egyéb paraméter #3 [EGYEB4]="" # Egyéb paraméter #4 ) # ---------------------------------------------------------- # További beállítások # ---------------------------------------------------------- tabs 4 # Kimenetben megjelenített tab-ok karakter mérete. shopt -s nocasematch # Kis- és nagybetűk figyelmen kívül hagyása a case szerkezetekben green=$(tput setaf 2) # Zöld kiemelőszín red=$(tput setaf 1) # Piros kiemelőszín cyan=$(tput setaf 6) # Cián kiemelőszín reset=$(tput sgr0) # Színek alapállapotba állítása escape_value="***" # "Menekülési érték", ha nincs megadva valamelyik kötelező érték VERSION="1.0" # Program verziója
Itt először deklaráljuk a PARAMS nevű asszociatív tömbünket, majd létrehozzuk benne a programunkban használt paraméterek számára a tároló elemeket, amiket alapértelmezett értékekkel töltünk fel (jelen esetben üres karakterláncokkal). Ezek a tömb elemek fogják tárolni a program futása során a kapott kapcsolókat, amiket utána kényelmesen elérhetünk. A Bash nem teszi szükségessé a tömbökben lévő elemek előre deklarálását, azonban érdemes megtenni már csak azért is, hogy a fájlt később megnyitva egyből láthassuk hogy milyen változókat/címkéket használtunk a programunkban. Így utólag is könnyebben átlátjuk a kódunkat, ha esetleg fél év múlva módosítanunk kell rajta.
Ezután a További beállítások soraiban elhelyezünk pár értékadást:
- tabs 4: Beállítjuk, hogy a programunk kimenetében a tab-okat a Bash mindig 4 karakter szélességben tegye ki.
- shopt -s nocasematch: Ezen shopt parancs segítségével beállítjuk, hogy a Bash ne különböztesse meg a program futása során a case elágazások kiértékeléseiben lévő kis- és nagybetűket. Ezáltal jelentősen csökkentjük a hibalehetőségek számát, mert így mindegy hogy kis- vagy nagybetűvel adják meg a programunknak a paramétereket.
- green, red, cyan, reset: Szín beállítások a tput parancs használatával. Ezek segítségével különböző színekkel emelhetjük ki a kimeneteinket. Erről a Hogyan használjunk színeket a terminálban című leírásban találhatunk részletes információt.
- escape_value: Ebben a változóban kell beállítanunk egy saját "menekülési értékünket" – amit biztosan nem fogunk kapni normál használat során a programban. Ennek célja, hogy a lentebbi Paraméterek előfeldolgozása részben az üres értékek helyére behelyettesítve a lenti részekben meg tudjuk különböztetni a kapcsoló teljes hiányát az értékek hiányától, hogy a kötelezően várt értékek elmaradása esetén hibát tudjunk generálni.
- VERSION: Itt pedig csak úgy adunk egy verziószámot a programunknak, amit majd a megfelelő kapcsolókkal le is tudunk kérdezni.
Függvénytár kialakítása
Szükségünk van olyan kódrészekre is, amiket újra fel tudunk használni programunk futása során. Ilyen például a cím kiírása, a súgó megjelenítése, a verzió kiírása, stb. Ezeket a részeket függvényekben helyezzük el, hogy a programunkban bárhonnan meg tudjuk őket hívni. Ehhez kialakítunk egy függvénytár részt, ahol a függvények szerepelnek. Így a forráskód könnyebben karbantartható marad. Kódunkat tehát a függvényekkel folytatjuk:
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 # ---------------------------------------------------------- # Függvénytár # ---------------------------------------------------------- # Cím kirakó függvény function print_title() { echo "Linuxportál példaprogram - Paraméterek rugalmas feldolgozása" } # Verzió kirakó függvény function print_version() { echo "Verzió: $VERSION" } # Súgó kirakó függvény function print_help() { cat << EOF Használat: $(basename $0) [kapcsoló] [érték] ... vagy $(basename $0) [kapcsoló]=[érték] ... Kapcsolók: -h, --help Súgó megjelenítése -d, --debug Debug mód. Ha meg van adva, akkor kiírja a futás során a használt összes kapcsoló változót. -v, --version Verzió lekérdezése -e1, --egyeb1 Egyéb 1 funkció futtatása -e2=x, --egyeb2=x Egyéb 2 tároló paraméter demonstrálása -e3, --egyeb3 Egyéb 3 logikai tároló paraméter demonstrálása -e4=x, --egyeb4=x Egyéb 4 tároló paraméter demonstrálása EOF } # "Kis" súgó kirakó függvény function print_help2() { echo "Súgó megjelenítése: $(basename $0) -h vagy --help" }
Itt tehát van négy függvényünk. Az első egyértelmű, ezek a címet és a program verzióját írják ki. A print_help() függvény pedig a súgót teszi ki. Itt a több soros és tabulált kimenet miatt a megszokott echo-k helyett egy heredoc-ban (Itt-dokumentum) tálaljuk a formázott kimenetet. így nem kell soronként bíbelődni az idézőjelekkel.
A súgóból az is kiderül, hogy a különböző funkciókat kétféle kapcsolóval is elérhetjük: rövid, illetve hosszú kapcsoló formában – mint ahogyan azt a legtöbb linuxos programnál is megszokhattuk. Továbbá az értékeket is kétféleképpen fogjuk feldolgozni, vagy egyenlőségjel megadással, vagy anélkül is elfogadja majd a programunk.
És végül a print_help2() függvényünk pedig csak egy praktikai célt szolgál: ha valahol hibát észlelünk a paraméterek feldolgozása során – például rosszul adta meg őket a felhasználó –, akkor a hibaüzenet után ezt a kis súgót tesszük ki. Ezzel jelezve, hogy hogyan jelenítheti meg a teljes súgót. Így a hibaüzenet nem kerüli el a figyelmet – szemben a fő súgóval, ami akár sok oldalas is lehet, emiatt a felhasználó könnyen szem elől tévesztheti a fontos hiba információkat.
Program indulása, paraméterek előfeldolgozása
Idáig elkészült minden fontos dolog, jöhet a program indulása. Ez igazából csak egy jelképes rész, itt írunk a kimenetre először, tehát itt történik érdemben valami elsőként:
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 # ---------------------------------------------------------- # Program indulás # ---------------------------------------------------------- print_title # kiírjuk a címet. Ezt minden esetben kitesszük # ---------------------------------------------------------- # Paraméterek előfeldolgozása # ---------------------------------------------------------- # Nulla paraméter vizsgálata if [[ $# -eq 0 ]] ; then # Ha nem kapott paramétert a program, akkor echo "${red}Hiba: Nincsenek paraméterek!${reset}" >&2 # Hibakimenetre írunk. print_help2 # Kis súgó kirakása exit 1 # és kilépés 1-es hibakóddal fi
A Paraméterek előfeldolgozása részben pedig elkezdjük az argumentumok vizsgálatát. Első körben megnézzük, hogy kapott-e a program kapcsolókat, paramétereket. Ha nem kapott egyet sem, akkor piros kiemeléssel kiírja a hibaüzenetet, amit a hibakimenetre (stderr) irányít, azután kiteszi az egy soros súgót, és kilép egy 1-es hibakóddal. Így nem is fut tovább a program.
Ha tehát most lefuttatjuk az idáig elkészült programot kapcsolók nélkül, akkor ilyen kimenetet kapunk:
Programunkat most egy while ciklussal folytatjuk, amiben elvégezzük a kapott paraméterek előfeldolgozását:
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 REMAIN_PARAMETERS=() # Maradék (nem várt) paraméterek tömbjének létrehozása while [[ $# -gt 0 ]] ; do # Ciklus indul, és megy, amíg maradt paraméter. parameter="$1" # Soron következő első paraméter kiolvasása need_shift=0 # "shift szükséges" flag nullázása. # Egyenlőség jeles paraméterek kezelése if [[ $parameter = *"="* ]]; then # Ha a soron következő első paraméter tartalmaz "=" jelet, akkor key=${parameter%%=*} # a kulcs lesz az első egyenlőség jel előtti rész, value=${parameter#*=} # az érték pedig az első egyenlőség jel utáni rész. else # Ha a paraméterben nem volt egyenlőség jel, akkor key=$parameter # a kulcs lesz maga a kapott paraméter value=$2 # és az utána lévő paraméter pedig lesz az esetleges érték. need_shift=1 # Itt jelezzük a lenti részeknek, hogy kell-e a plusz paraméter forgatás. fi # Kapcsolók elágazása case $key in # case szerkezet: megvizsgáljuk a kulcsot, hogy mit tartalmaz. -h|--help) # Súgó (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[HELP]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -d|--debug) # debug (logikai) kapcsoló ékezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[DEBUG]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -v|--version) # verzió (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[VERSION]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -e1|--egyeb1) # egyeb1 (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[EGYEB1]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -e2|--egyeb2) # egyeb1 (értéket tároló) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket [[ "$value" == "" ]] && value=$escape_value # Ha nem kapott értéket ez a kapcsoló, akkor beletesszük a menekülési értéket PARAMS[EGYEB2]=$value # a fentebb megállapított érték tárolása az ennek fentartott tömb elemében. [[ "$need_shift" == 1 ]] && shift # Ha szükséges a paraméterek forgatása, akkor még egyszer forgatjuk. ;; -e3|--egyeb3) # egyeb3 (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[EGYEB3]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -e4|--egyeb4) # egyeb4 (értéket tároló) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket [[ "$value" == "" ]] && value=$escape_value # Ha nem kapott értéket ez a kapcsoló, akkor beletesszük a menekülési értéket PARAMS[EGYEB4]=$value # a fentebb megállapított érték tárolása az ennek fentartott tömb elemében. [[ "$need_shift" == 1 ]] && shift # Ha szükséges a paraméterek forgatása, akkor még egyszer forgatjuk. ;; *) # Ha ismeretlen paraméter jön a sorban, akkor shift # Shift-eljük (forgatjuk) a paramétereket REMAIN_PARAMETERS+=("$key") # Felvesszük a kapott paramétert a maradék (nem várt) paraméterek tömbjébe. ;; esac # case vége done # ciklus vége # Idáig tehát "elfogyasztottuk" az összes paramétert a shift-ek által. # Beállítjuk a megmaradt egyéb paramétereket a szabványos $1, $2, $3, stb változókba # Így könnyebb a maradékot feldolgozni később, amennyiben szükséges. set -- "${REMAIN_PARAMETERS[@]}"
Ennek a résznek az elején létrehozunk egy REMAIN_PARAMETERS nevű tömböt, amiben az előfeldolgozás során gyűjtjük a nem várt paramétereket, az esetleges későbbi feldolgozás céljából.
Ezután elindítunk egy while ciklust, ami addig tart, amíg el nem fogynak a program paraméterei. Ennek a logikája abból áll, hogy először megvizsgáljuk, hogy a soron következő első paraméter tartalmaz-e egyenlőségjelet. Ha van benne egyenlőségjel, akkor az mentén kettébontjuk a paramétert; az első része lesz a kulcs, majd az egyenlőségjel utáni része pedig az érték. Ebben az esetben egyetlen paraméterben kaptuk meg a kapcsolót és az értékét is, mivel nem választotta őket szét szóköz. Ilyenkor csak egyszer kell majd megforgatnunk az argumentumokat a shift paranccsal a lentebbi case részben, mivel csak egy paramétert használunk fel. A másik eset, amikor a kapott paraméter nem tartalmaz egyenlőségjelet. Ilyenkor vagy csak egy logikai kapcsolót kaptunk, aminek nem kell érték, hanem elég csak a jelenléte, vagy pedig az utána lévő paraméter lesz maga az értéke. Tehát itt egyszer mindenképpen kell forgatnunk a paramétereket, ami első körben "eltünteti" a kulcsot. És ha a kapcsolónak értéket is kell hordoznia, ami miatt felhasználjuk a következő paramétert is, akkor ebben az esetben még egyszer meg kell forgatnunk a paraméterek listáját, tehát el kell tüntetnünk még egy paramétert a lenti részben. Majd indul az egész előröl, amíg el nem fogynak a paraméterek.
Az ez utáni case szerkezet még mindig az előfeldolgozás része, mivel itt még nem történik műveletvégzés, hanem csak a paraméterek forgatását végezzük és a kapcsolók értékeit tároljuk. Röviden sorban haladva a példa kapcsolóin:
- -h vagy --help: Ez egy logikai kapcsoló, nem kell neki érték. Itt forgatunk egyet a paramétereken a shift paranccsal, majd beállítjuk a logikai 1 értéket a megfelelő változóba.
- -d vagy --debug, -v vagy --version és -e1 vagy --egyeb1: Ez a három kapcsoló is ugyanígy működik. Shift-elés, majd a megfelelő tömb elem 1-re állítása
- -e2 vagy --egyeb2: Ez egy értéket hordozó kapcsoló, először itt is shift-elünk, ezután ha nincs érték a $value változóban, akkor betöltjük a program elején beállított menekülési értéket, ezáltal a lentebbi műveletek kidolgozásánál érzékelni tudjuk az érték nélkül maradt kapcsolót, hogy ilyenkor hibát dobhassunk.
Ezután a megfelelő tömb elembe betöltjük a $value értékét – ami vagy a fentebb beállított értéket, vagy a menekülési értéket tartalmazza –, majd még egyszer shift-eljük a paramétereket, ha nem volt egyenlőségjel, tehát fel kellett használnunk a következő paramétert is értékként. - -e3 vagy --egyeb3: Ez is logikai kapcsoló, mint az első 4.
- -e4 vagy --egyeb4: Ez pedig szintén egy értéket hordozó kapcsoló, mint a -e2 vagy --egyeb2, tehát itt is ugyanígy járunk el.
- *: Ha pedig olyan paramétert kaptunk, ami nem illeszkedik a fentiek egyikére sem, akkor ez az ág teljesül, ahol szintén forgatunk egyet a paramétereken, majd betesszük az iménti ismeretlen argumentumot a nem várt paraméterek tömbjébe.
Ezzel a mechanizmussal egyszerre négy dolgot is elérünk:
- A kapcsolók sorrendje nem számít, mivel az előfeldolgozó rész végigjárta az összeset, és beállítgatta a megfelelő tömb elemekbe a megfelelő értékeket (logikai 1-eket, vagy egyedi értékeket).
- Értéket lehet adni egy paraméternek egyenlőségjellel is, és anélkül is (szóközzel elválasztva)
- A kapcsolókat rövid és hosszú neveikkel is lehet használni, tetszés szerint.
- Összetettebb kapcsolókat (pl. egymástól függő, vagy egymást kiegészítő kapcsolókat) is használhatunk, mivel már végigjártuk az összeset, ezért egyben ki tudjuk őket értékelni.
Ennek a résznek a legutolsó sorában pedig visszaállítjuk a nem várt paramétereket a program fő paramétereinek helyére. Így szükség esetén később még használhatjuk őket bármire.
Program futása
Ez után már jöhet a program futási része, ami lényegében az előfeldolgozott változók alapján elvégzi a szükséges műveleteket:
Debug mód
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 # ---------------------------------------------------------- # Program futás # ---------------------------------------------------------- # Debug mód. Ezt vizsgáljuk legelőször, hogy bármikor le tudjon futni if [[ "${PARAMS[DEBUG]}" == 1 ]]; then echo $cyan echo "**********************************************" echo "DEBUG mód" echo "**********************************************" echo "Várt paraméterek:" for key in "${!PARAMS[@]}"; do echo "$key: ${PARAMS[$key]}" ; done echo echo "Nem várt paraméterek: ${REMAIN_PARAMETERS[@]}" echo "**********************************************" echo $reset fi
Itt a debug módot láthatjuk, ami egy ciklusban bejárja a PARAMS tömböt és kiírja annak kulcsait és értékeit, amiket az előfeldolgozó részben töltöttünk fel a kapott opciókkal, valamint a nem várt paramétereket is felsorolja – amennyiben a futtatásnál használjuk a -d vagy --debug opciót:
Súgó megjelenítése
A következő feltételben a súgót jelenítjük meg:
171 172 173 174 175 176 # Súgó megjelenítése if [[ "${PARAMS[HELP]}" == 1 ]]; then print_help echo exit 0 fi
Itt a PARAMS tömb HELP logikai értékét figyelve hívjuk a súgó függvényünket, majd ki is lépünk. Ez után már nem futhat le semmilyen más funkció, akármilyen kapcsolókat adtunk is meg (kivéve a fentebb már feldolgozott debug mód):
Verzió megjelenítése
Ugyanígy ellenőrizzük a verziószámot megjelenítő kapcsolóhoz tartozó változót, és ennek megfelelően meghívjuk a verziószámot megjelenítő függvényünket:
179 180 181 182 183 184 # Verziószám megjelenítése if [[ "${PARAMS[VERSION]}" == 1 ]]; then print_version echo exit 0 fi
Egyéb műveletek kivitelezései
Logikai kapcsolók
Az "Egyéb 1" logikai kapcsoló kivitelezése a következőképpen történik:
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 # Egyéb 1 logikai kapcsoló kiértékelése és a hozzá tartozó művelet végrehajtása if [[ "${PARAMS[EGYEB1]}" == 1 ]]; then # Itt csak logikai 1 értéket vizsgálunk # Saját műveletünk végrehajtása... echo "Egyéb 1 logikai kapcsoló érzékelve, művelet végrehajtása..." # Ez ad valamilyen kimenetet... # ... # ... # Művelet utáni hiba kiértékelés result=$? # Hibakód kiolvasása if (( $result == 0 )); then # Hiba kiértékelése: Ha nem volt hiba echo "${green}Az 'Egyéb 1' művelet sikeresen lefutott.${reset}" # exit 0 # Itt saját ízlés szerint kilépünk, # ha nem szeretnénk további műveleteket végrehajtani ez után. else # Ha hiba lépett fel a műveletünk során, akkor echo "${red}Hiba történt az 'Egyéb 1' művelet során!${reset}" >&2 exit 10 # Kilépünk egy magunk által meghatározott hibakóddal fi echo # Üres sor kihagyása fi
Logikai kapcsoló lévén itt csak magának a kapcsolónak a jelenlétét vizsgáljuk, azaz az előfeldolgozó részben betöltött 1-es értéket a megfelelő tömb indexben. Ha ez megvan, akkor lefuttatjuk a kívánt műveletünket, majd kiértékeljük annak eredményességét, és ennek megfelelően adjuk a visszajelzést:
- A művelet sikeres futása után az első ágban zöld szövegkiemeléssel értesítjük a felhasználót a művelet eredményeiről. Itt még szükség szerint használhatjuk az exit parancsot is, attól függően, hogy szeretnénk-e hogy a művelet után leálljon-e a program futása, vagy inkább engedjük tovább a működését. A példában ezt kikommenteztem, hogy egy futtatással tudjuk figyelni az összes műveletet egyszerre.
- A kiértékelés hibás ágában pedig piros kiemeléssel értesítjük a felhasználót a hiba kimeneten, majd megszakítjuk a program futását egy egyedi hibakóddal.
Végül a kimenet áttekinthetőségének kedvéért hagyjunk ki egy üres sort. Sajnos ezzel a legtöbb Linux parancsnál nem foglalkoznak, így összeolvad minden. Én ezért igyekszem a lehető legtöbb helyen színeket használni, és szellősebben, tagoltabban, áttekinthetőbben tálalni a kimenetet, hogy egy hosszabb ideig tartó terminálban végzett munka se legyen a szemünknek olyan kimerítő. Egy színtelen, ingerszegény terminálban való ténykedés során figyelmünk is hamarabb gyengül.
Az "Egyéb 3" logikai kapcsoló működési elve ugyanez, így most itt nem részletezem.
Értéket továbbító kapcsolók
Az "Egyéb 2" értéket továbbító kapcsoló kidolgozása pedig az alábbiak szerint történik:
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 # Egyéb 2 kapcsoló kiértékelése és a hozzá tartozó művelet végrehajtása if [[ "${PARAMS[EGYEB2]}" != "" ]]; then # Ha bármilyen érték van benne, lefut ez a rész # Kötelező érték vizsgálata. Ez a rész opcionális, amennyiben mindenképpen várunk valamilyen értéket ebben a kapcsolóban. if [[ "${PARAMS[EGYEB2]}" == "$escape_value" ]]; then # Ha a változóban a menekülési érték van benne, akkor hibával leállunk. echo "${red}Hiba: Az 'Egyéb 2' kapcsoló érzékelve, de nem kapott értéket!${reset}" >&2 exit 20 # Kilépünk egy magunk által meghatározott hibakóddal fi # Saját műveletünk végrehajtása... echo "Egyéb 2 kapcsoló érzékelve, művelet végrehajtása..." # Ez ad valamilyen kimenetet... echo "A kapott érték: '${PARAMS[EGYEB2]}'" # Kiírjuk a kapott értéket. # ... # ... # Művelet utáni hiba kiértékelés result=$? # Hibakód kiolvasása if (( $result == 0 )); then # Hiba kiértékelése: Ha nem volt hiba echo "${green}Az 'Egyéb 2' művelet sikeresen lefutott.${reset}" # exit 0 # Itt saját ízlés szerint kilépünk, # ha nem szeretnénk további műveleteket végrehajtani ez után. else # Ha hiba lépett fel a műveletünk során, akkor echo "${red}Hiba történt az 'Egyéb 2' művelet során!${reset}" >&2 exit 21 # Kilépünk egy magunk által meghatározott hibakóddal fi echo # Üres sor kihagyása fi
Ennek a kivitelezése annyiban tér el a logikai kapcsolóétól, hogy itt a fő feltételünkben az üres karakterlánctól eltérő eseteket figyeljük. A program elején a PARAM tömbbe amikor beállítottuk az alapértelmezett értékeket, a különböző kapcsolók változói ekkor kapták meg az üres karakterlánc "" értéket. így tehát alapból létezik a tömb megfelelő eleme, de nincs benne semmi. Ezért amikor a program kap egy ilyen kapcsolót, és értéket ad neki, akkor az az érték kerül bele. Ha viszont lemarad az érték egy ilyen kapcsolóról, akkor kerül bele a menekülési érték.
Így tehát először megvizsgáljuk, hogy egyáltalán megkapta-e a program ezt a kapcsolót (ürestől bármi eltérő), majd utána a 213. sorban megvizsgáljuk a benne lévő értéket: ha a menekülési értékünk van benne, akkor az azt jelenti, hogy nem adták meg az értékét ennek a kapcsolónak. ilyenkor hibát generálunk, mivel ettől tér el a logikai kapcsolótól, hogy értéket is kell neki kapnia: piros kiemeléssel a hibakimenetre írunk, majd egy egyedi hibakóddal megszakítjuk a program futását.
És innentől a történet már ugyanaz, mint a logikai kapcsolóknál: saját művelet végrehajtása, hiba kiértékelése és kiírás a kimenetre.
Az "Egyéb 4" értéket továbbító kapcsoló működési elve ugyanez, így most itt ezt sem részletezem.
Működési példák
A következő példákban jól láthatók a program fentebb leírt viselkedése:
Az első futtatásnál láthatjuk a következőket:
- A kapcsolók/paraméterek mindegy milyen sorrendben érkeznek.
- A kapcsolók használhatók a rövid és a hosszú neveikkel
- A kapcsolóknak átadhatunk értéket szóközzel is és egyenlőségjellel is.
- A megadott érték is tartalmazhat további egyenlőségjelet, nem zavarja meg a működését.
A második futtatásnál pedig az "Egyéb 4" kapcsolónak szándékosan üres értéket "" adtam meg, így láthatjuk a hibakezelését is.
Az alábbi képen pedig láthatjuk, hogy a kapcsolók kis- és nagybetűkkel is működnek:
Teljes példaprogram
Itt pedig egyben láthatjuk a teljes példaprogramot, ha esetleg ki szeretnénk próbálni, innen könnyebben kimásolhatjuk:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 #!/bin/bash # ------------------------------------------------------------------------------ # Linuxportál példaprogram: Paraméterek rugalmas feldolgozása # ------------------------------------------------------------------------------ # ---------------------------------------------------------- # Kapcsolók/paraméterek alapértelmezett értékei # ---------------------------------------------------------- declare -A PARAMS PARAMS=( [DEBUG]="" # Debug mód [HELP]="" # súgó megjelenítése [VERSION]="" # Verzió információk [EGYEB1]="" # Egyéb paraméter #1 [EGYEB2]="" # Egyéb paraméter #2 [EGYEB3]="" # Egyéb paraméter #3 [EGYEB4]="" # Egyéb paraméter #4 ) # ---------------------------------------------------------- # További beállítások # ---------------------------------------------------------- tabs 4 # Kimenetben megjelenített tab-ok karakter mérete. shopt -s nocasematch # Kis- és nagybetűk figyelmen kívül hagyása a case szerkezetekben green=$(tput setaf 2) # Zöld kiemelőszín red=$(tput setaf 1) # Piros kiemelőszín cyan=$(tput setaf 6) # Cián kiemelőszín reset=$(tput sgr0) # Színek alapállapotba állítása escape_value="***" # "Menekülési érték", ha nincs megadva valamelyik kötelező érték VERSION="1.0" # Program verziója # ---------------------------------------------------------- # Függvénytár # ---------------------------------------------------------- # Cím kirakó függvény function print_title() { echo "Linuxportál példaprogram - Paraméterek rugalmas feldolgozása" } # Verzió kirakó függvény function print_version() { echo "Verzió: $VERSION" } # Súgó kirakó függvény function print_help() { cat << EOF Használat: $(basename $0) [kapcsoló] [érték] ... vagy $(basename $0) [kapcsoló]=[érték] ... Kapcsolók: -h, --help Súgó megjelenítése -d, --debug Debug mód. Ha meg van adva, akkor kiírja a futás során a használt összes kapcsoló változót. -v, --version Verzió lekérdezése -e1, --egyeb1 Egyéb 1 funkció futtatása -e2=x, --egyeb2=x Egyéb 2 tároló paraméter demonstrálása -e3, --egyeb3 Egyéb 3 logikai tároló paraméter demonstrálása -e4=x, --egyeb4=x Egyéb 4 tároló paraméter demonstrálása EOF } # "Kis" súgó kirakó függvény function print_help2() { echo "Súgó megjelenítése: $(basename $0) -h vagy --help" } # ---------------------------------------------------------- # Program indulás # ---------------------------------------------------------- print_title # kiírjuk a címet. Ezt minden esetben kitesszük # ---------------------------------------------------------- # Paraméterek előfeldolgozása # ---------------------------------------------------------- # Nulla paraméter vizsgálata if [[ $# -eq 0 ]] ; then # Ha nem kapott paramétert a program, akkor echo "${red}Hiba: Nincsenek paraméterek!${reset}" >&2 # Hibakimenetre írunk. print_help2 # Kis súgó kirakása exit 1 # és kilépés 1-es hibakóddal fi REMAIN_PARAMETERS=() # Maradék (nem várt) paraméterek tömbjének létrehozása while [[ $# -gt 0 ]] ; do # Ciklus indul, és megy, amíg maradt paraméter. parameter="$1" # Soron következő első paraméter kiolvasása need_shift=0 # "shift szükséges" flag nullázása. # Egyenlőség jeles paraméterek kezelése if [[ $parameter = *"="* ]]; then # Ha a soron következő első paraméter tartalmaz "=" jelet, akkor key=${parameter%%=*} # a kulcs lesz az első egyenlőség jel előtti rész, value=${parameter#*=} # az érték pedig az első egyenlőség jel utáni rész. else # Ha a paraméterben nem volt egyenlőség jel, akkor key=$parameter # a kulcs lesz maga a kapott paraméter value=$2 # és az utána lévő paraméter pedig lesz az esetleges érték. need_shift=1 # Itt jelezzük a lenti részeknek, hogy kell-e a plusz paraméter forgatás. fi # Kapcsolók elágazása case $key in # case szerkezet: megvizsgáljuk a kulcsot, hogy mit tartalmaz. -h|--help) # Súgó (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[HELP]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -d|--debug) # debug (logikai) kapcsoló ékezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[DEBUG]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -v|--version) # verzió (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[VERSION]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -e1|--egyeb1) # egyeb1 (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[EGYEB1]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -e2|--egyeb2) # egyeb1 (értéket tároló) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket [[ "$value" == "" ]] && value=$escape_value # Ha nem kapott értéket ez a kapcsoló, akkor beletesszük a menekülési értéket PARAMS[EGYEB2]=$value # a fentebb megállapított érték tárolása az ennek fentartott tömb elemében. [[ "$need_shift" == 1 ]] && shift # Ha szükséges a paraméterek forgatása, akkor még egyszer forgatjuk. ;; -e3|--egyeb3) # egyeb3 (logikai) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket PARAMS[EGYEB3]=1 # Logikai érték beállítása az ennek fentartott tömb elemében. ;; -e4|--egyeb4) # egyeb4 (értéket tároló) kapcsoló érkezett... shift # Shift-eljük (forgatjuk) a paramétereket [[ "$value" == "" ]] && value=$escape_value # Ha nem kapott értéket ez a kapcsoló, akkor beletesszük a menekülési értéket PARAMS[EGYEB4]=$value # a fentebb megállapított érték tárolása az ennek fentartott tömb elemében. [[ "$need_shift" == 1 ]] && shift # Ha szükséges a paraméterek forgatása, akkor még egyszer forgatjuk. ;; *) # Ha ismeretlen paraméter jön a sorban, akkor shift # Shift-eljük (forgatjuk) a paramétereket REMAIN_PARAMETERS+=("$key") # Felvesszük a kapott paramétert a maradék (nem várt) paraméterek tömbjébe. ;; esac # case vége done # ciklus vége # Idáig tehát "elfogyasztottuk" az összes paramétert a shift-ek által. # Beállítjuk a megmaradt egyéb paramétereket a szabványos $1, $2, $3, stb változókba # Így könnyebb a maradékot feldolgozni később, amennyiben szükséges. set -- "${REMAIN_PARAMETERS[@]}" # ---------------------------------------------------------- # Program futás # ---------------------------------------------------------- # Debug mód. Ezt vizsgáljuk legelőször, hogy bármikor le tudjon futni if [[ "${PARAMS[DEBUG]}" == 1 ]]; then echo $cyan echo "**********************************************" echo "DEBUG mód" echo "**********************************************" echo "Várt paraméterek:" for key in "${!PARAMS[@]}"; do echo "$key: ${PARAMS[$key]}" ; done echo echo "Nem várt paraméterek: ${REMAIN_PARAMETERS[@]}" echo "**********************************************" echo $reset echo # Üres sor kihagyása fi # Súgó megjelenítése if [[ "${PARAMS[HELP]}" == 1 ]]; then print_help echo exit 0 fi # Verziószám megjelenítése if [[ "${PARAMS[VERSION]}" == 1 ]]; then print_version echo exit 0 fi # Egyéb 1 logikai kapcsoló kiértékelése és a hozzá tartozó művelet végrehajtása if [[ "${PARAMS[EGYEB1]}" == 1 ]]; then # Itt csak logikai 1 értéket vizsgálunk # Saját műveletünk végrehajtása... echo "Egyéb 1 logikai kapcsoló érzékelve, művelet végrehajtása..." # Ez ad valamilyen kimenetet... # ... # ... # Művelet utáni hiba kiértékelés result=$? # Hibakód kiolvasása if (( $result == 0 )); then # Hiba kiértékelése: Ha nem volt hiba echo "${green}Az 'Egyéb 1' művelet sikeresen lefutott.${reset}" # exit 0 # Itt saját ízlés szerint kilépünk, # ha nem szeretnénk további műveleteket végrehajtani ez után. else # Ha hiba lépett fel a műveletünk során, akkor echo "${red}Hiba történt az 'Egyéb 1' művelet során!${reset}" >&2 exit 10 # Kilépünk egy magunk által meghatározott hibakóddal fi echo # Üres sor kihagyása fi # Egyéb 2 kapcsoló kiértékelése és a hozzá tartozó művelet végrehajtása if [[ "${PARAMS[EGYEB2]}" != "" ]]; then # Ha bármilyen érték van benne, lefut ez a rész # Kötelező érték vizsgálata. Ez a rész opcionális, amennyiben mindenképpen várunk valamilyen értéket ebben a kapcsolóban. if [[ "${PARAMS[EGYEB2]}" == "$escape_value" ]]; then # Ha a változóban a menekülési érték van benne, akkor hibával leállunk. echo "${red}Hiba: Az 'Egyéb 2' kapcsoló érzékelve, de nem kapott értéket!${reset}" >&2 exit 20 # Kilépünk egy magunk által meghatározott hibakóddal fi # Saját műveletünk végrehajtása... echo "Egyéb 2 kapcsoló érzékelve, művelet végrehajtása..." # Ez ad valamilyen kimenetet... echo "A kapott érték: '${PARAMS[EGYEB2]}'" # Kiírjuk a kapott értéket. # ... # ... # Művelet utáni hiba kiértékelés result=$? # Hibakód kiolvasása if (( $result == 0 )); then # Hiba kiértékelése: Ha nem volt hiba echo "${green}Az 'Egyéb 2' művelet sikeresen lefutott.${reset}" # exit 0 # Itt saját ízlés szerint kilépünk, # ha nem szeretnénk további műveleteket végrehajtani ez után. else # Ha hiba lépett fel a műveletünk során, akkor echo "${red}Hiba történt az 'Egyéb 2' művelet során!${reset}" >&2 exit 21 # Kilépünk egy magunk által meghatározott hibakóddal fi echo # Üres sor kihagyása fi # Egyéb 3 logikai kapcsoló kiértékelése és a hozzá tartozó művelet végrehajtása if [[ "${PARAMS[EGYEB3]}" == 1 ]]; then # Itt csak logikai 1 értéket vizsgálunk # Saját műveletünk végrehajtása... echo "Egyéb 3 logikai kapcsoló érzékelve, művelet végrehajtása..." # Ez ad valamilyen kimenetet... # ... # ... # Művelet utáni hiba kiértékelés result=$? # Hibakód kiolvasása if (( $result == 0 )); then # Hiba kiértékelése: Ha nem volt hiba echo "${green}Az 'Egyéb 3' művelet sikeresen lefutott.${reset}" # exit 0 # Itt saját ízlés szerint kilépünk, # ha nem szeretnénk további műveleteket végrehajtani ez után. else # Ha hiba lépett fel a műveletünk során, akkor echo "${red}Hiba történt az 'Egyéb 3' művelet során!${reset}" >&2 exit 30 # Kilépünk egy magunk által meghatározott hibakóddal fi echo # Üres sor kihagyása fi # Egyéb 4 kapcsoló kiértékelése és a hozzá tartozó művelet végrehajtása if [[ "${PARAMS[EGYEB4]}" != "" ]]; then # Ha bármilyen érték van benne, lefut ez a rész # Kötelező érték vizsgálata. Ez a rész opcionális, amennyiben mindenképpen várunk valamilyen értéket ebben a kapcsolóban. if [[ "${PARAMS[EGYEB4]}" == "$escape_value" ]]; then # Ha a változóban a menekülési érték van benne, akkor hibával leállunk. echo "${red}Hiba: Az 'Egyéb 4' kapcsoló érzékelve, de nem kapott értéket!${reset}" >&2 exit 40 # Kilépünk egy magunk által meghatározott hibakóddal fi # Saját műveletünk végrehajtása... echo "Egyéb 4 kapcsoló érzékelve, művelet végrehajtása..." # Ez ad valamilyen kimenetet... echo "A kapott érték: '${PARAMS[EGYEB4]}'" # Kiírjuk a kapott értéket. # ... # ... # Művelet utáni hiba kiértékelés result=$? # Hibakód kiolvasása if (( $result == 0 )); then # Hiba kiértékelése: Ha nem volt hiba echo "${green}Az 'Egyéb 4' művelet sikeresen lefutott.${reset}" # exit 0 # Itt saját ízlés szerint kilépünk, # ha nem szeretnénk további műveleteket végrehajtani ez után. else # Ha hiba lépett fel a műveletünk során, akkor echo "${red}Hiba történt az 'Egyéb 4' művelet során!${reset}" >&2 exit 41 # Kilépünk egy magunk által meghatározott hibakóddal fi echo # Üres sor kihagyása fi
Konklúzió
Íme tehát egy shell szkript, ami rugalmasan tudja kezelni a kapott paramétereket, valamint komplett hibakezelést is tartalmaz. Persze ez még fűszerezhető sok mindennel, de ez már a felhasználási területen is múlik, hogy milyen célra szeretnénk elkészíteni programunkat. Célszerű eltenni sablonnak, és az újonnan készülő programjainkat csak ebbe építeni, és már készen is van a paraméterek kezelése is.
- A hozzászóláshoz regisztráció és bejelentkezés szükséges
- 961 megtekintés