[[oktatas:linux|< Linux]] ====== Shell programozás ====== * **Szerző:** Sallai András * Copyright (c) 2011, Sallai András * Szerkesztve: 2011-2021 * Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC BY-SA 4.0]] * Web: https://szit.hu ===== Bevezetés ===== A shell programok végrehajtható parancsok gyűjteménye, vagyis egymás után írt parancsok listája egy állományban. Nevezhetjük egyszerűen shell scriptnek is. A shell scripteknek nem kötelező kiterjesztést megadni. Ha mégis szeretnénk megadni akkor válasszuk az .sh kiterjesztést. A rendszer azonban nem a kiterjesztésből fogja eldönteni, hogy shell script vagy nem az. Ha egy fájlba parancsokat írunk, azt shell scriptként futtatva a parancsok végrehajtódnak. A unixos rendszerekben a scripteket egy karakterpárral kezdjük: #! A rendszer ebből tudja, hogy valamilyen scriptről van szó. Ha egy hexa szerkesztővel nézzük: hexedit scriptnev Akkor azt mondhatjuk a következő kódú karakterrel kezdődik: 23 21 A unix alapú rendszerekben a futtatható fájloknak sincs kiterjesztése alapértelmezetten. A Linux például ELF binárisokat futtat. Egy ELF bináris fájl eleje mindig a következő byte sorozattal kezdődik: 7F 45 4C 46 Nézzük meg például az ls parancsunkat (Felhasználóként! Ne rendszergazdaként!). hexedit /bin/ls Kilépés Ctrl + C Ha a hexedit nem lenne telepítve: apt install hexedit A scriptek, tehát #! kezdődnek, de honnan tudja a rendszer, hogy milyen scriptről van szó? A #! után adjuk meg az interpreter nevét és elérési útját. Perl script esetén például: #!/usr/bin/perl Bash shell script esetén, például: #!/bin/bash Python script esetén, például: #!/usr/bin/python ===== Első lépések ===== * Szükségünk van egy szövegszerkesztőre, amiben megírjuk a scriptet. * Futtathatóvá kell tennünk a scriptet. * Szintaktika: * chmod jogok script-neve * Példa: * chmod +x bar * chmod 755 bar * Futtatjuk a scriptet * Szintaktika: * bash script-neve * sh script-neve * ./script-neve * Példa: * $ bash bar * $ sh bar * $ ./bar Megjegyzés Az utolsó szintaktikában a ./ jelentése: az aktuális könyvtár, de csak ez. A . (dot) karaktert használhatjuk önmagában is, ez induláskor nem hoz létre egy új shell másolatot. Szintaktika: . script-neve Példa: $ . foo Ha egy olyan scriptet indítunk amelyik nem áll le azonnal (például vár adatbevitelre), akkor meggyőződhetünk a fentiekről ha futtatjuk a felhasználó nevén a pstree parancsot. Legyen a példa kedvéért egy joska nevű felhasználó aki a ker.sh nevű scriptet indítja. A scriptet ./ker.sh vagy . ker.sh formában indítja, ekkor lekérdezzük a folyamatfát: pstree joska A lehetséges kimenet ./ker.sh esetén: bash───pstree bash───ker.sh A lehetséges kimenet . ker.sh esetén: bash───pstree bash A különbséget a ps parancs is megmutatja: ps u Kapcsolónak csak a "u" kapcsolót kell megadni. Azt már tudjuk, hogy egy shell sciptet mindig "#!" karakterekkel kezdjük és utána leírjuk az értelmező útvonalát. De hogyan deríthatő ki az értelmező útvonala? Mi most, Bash shell-t szeretnénk használni. A which parancs megmondja, hogy honnan fut egy parancs: which bash ===== Megjegyzés ===== #!/bin/bash # egy soros megjegyzés : ' Több soros megjegyzés ' A (:) kettőspont után szóköz szükséges. ===== Kivitel ===== Hozzuk létre első "Helló Világ!" nevű shell scriptünket. készítsünk egy elso.sh nevű fájlt, melynek tartalma a következő: #!/bin/bash echo "Helló Világ!" Adjunk rá futtatási jogot: chmod +x elso.sh Futtassuk: ./elso.sh Ezek után minden további scriptünket így kell létrehoznunk. Sorvége: echo -n "Helló Világ!" Utóbbi utasítás a -n hatására nem ír a sorvégére sortörést. ===== Escape szekvenciák ===== Az "Escape" karakterek engedélyezése: echo -e "Helló Világ!" A -e kapcsoló engedélyezi az escape karakterek értelmezését a karakterláncokban: * \a csengő * \b egy karakter törlése visszafelé * \c nem ír ki újsor karaktert * \f lapdobás * \n új sor * \r kocsi vissza * \t vízszintes tabulátor * \v függőleges tabulátor * \\ backslash * \0nnn a karakter ASCII kódja nnn (oktálisan) * \xnnn a karakter ASCII kódja nnn (hexadecimálisan) Az escape karakter a normál karakter egy speciális értelmezése. Az "a" például csengőhang, az "n" sortörés. Azt, hogy speciális escape értelmezés következik egy "\" visszaperjel karakterrel mondjuk meg. A következő parancs például a "Helló" és a "Világ!" szöveget külön sorba írja: echo -e "Helló\nVilág!" LXTerminalban: Szerkesztés > Beállítások > Stílus > Hallható csengőhang [✔] ===== Változók és az értékadás ===== A változók kis és nagybetű érzékenyek. A SZAM és a szam változó két különböző változó. Az átláthatóság érdekében a változóneveket nagybetűvel szokás írni. SZAM=3 SZOVEG="alma" echo $SZAM echo $SZOVEG Az értékadó operátor előtt és után nem hagyhatunk whitespace karaktert, mert akkor a SZAM vagy SZOVEG változót egy parancsnak venné. ===== Változó deklaráció ===== #!/bin/bash declare -i szam szam=4 szam=szam+1 szam+=1 echo $szam #!/bin/bash declare -i szam=4 szam=szam+1 szam+=1 echo $szam ===== Formázott kimenet ===== A printf utasítással a kiírt változókat, értékeket több módon formázhatjuk. Elsőként lássunk egy szimpla változó kiíratást: printf "%s\n" $HOME printf "%s\n" alma printf "%d\n" 35 Szélesség megadása: printf "%10d\n" 35 Vezető nullák: printf "%0d\n" 35 printf "%f\n" 35,12345 Tizedesjegyek megadása: printf "%.2f\n" 35,12345 Egyszerre több: printf "%010.2f\n" 35,12345 A prrintf segítségével színezhetjük is a kimenetet: printf "\033[0;32mszínes szöveg\n" printf "\033[1;33mszínes szöveg\n" Meg kell jegyezzem, az echo parancs a -e kapcsolóval értelmezi az escape szekvenciákat, és ugyanez a hatás elérhető: echo -e "\033[1;33mszínes szöveg\n" Unicode karakter kiíratása is lehetséges a \U escape szekvenciával. Lássuk az "üres" szót: printf "%b" "Unicode Character (U+7a7a) \U7a7a \n" A "U" nagy u helyett használhatunk "u" kis u-t is. Leghosszabb kiírható számjegy: 1234567890123456789 ^ Formátumkód ^ Leírás ^ | d | Egész szám kiíratás | | f | Valós szám kiíratás | | s | String kiíratás | ^ Jelző ^ Leírás ^ | 0 | vezető nullák | | - | balra igazítás | | + | előjel | ===== Véletlen generálás ===== ==== Véletlen szám ==== Az alábbiakban többféle véletlen szám előállítási lehetőséget ismerhetünk meg. echo $RANDOM n=$RANDOM echo $n 1 és 3 között egy szám: echo $((RANDOM % 3 + 1)) 0 és 4 közötti számot véletlenszerűen: r=$RANDOM R=$((r %= 5)) echo $R 0 és 4 közötti szám: echo $(( $(od -An -N2 -i /dev/random) % 5)) Véletlenszám az od paranccsal: od -A n -N 1 -t d < /dev/urandom A -t d decimális formában kiírásról gondoskodik. A -N 1, egyetlen bájt vételét írja elő. shuf -i 1-10 -n 1 Ha telepítve van a perl nyelv: perl -e 'print int rand 10, "\n"; ' ==== Véletlen bemenet ==== A shuf a bemenetére kapott sorokból véletlenszerűen választ, majd kiírja a kimenetre: echo -e "1\n2\n3\n4\n5" | shuf -n 1 könyvtár tartalmából véletlenszerűen választás: ls dir1 | shuf -n 1 ===== Kifejezés kiértékelése ===== expr 1 + 3 Az 1+3 összegét, azaz 4-et ad vissza #!/bin/bash A=3 B=7 expr $A + $B #!/bin/bash A=3 B=7 C=`expr $A + $B` echo $C #!/bin/bash A=3 B=7 C=`expr $A "*" $B` echo $C #!/bin/bash A=3 B=7 C=`expr \( $A "*" $B \) / 2 ` echo $C #!/bin/bash A=3 B=7 C=`echo \( $A "*" $B \) / 2 | bc` echo $C Gyökvonás: #!/bin/bash echo sqrt\(9\) | bc Hatványozás: #!/bin/bash echo 2 ^ 8 | bc ==== Dupla zárójel ==== A következő program a szam változó értéket 1-gyel növeli: #!/bin/bash szam=3 szam=$((szam+1)) echo $szam Variációkat láthatunk a növelésre: szam=$((szam+1)) ((szam=szam+1)) ((szam+=1)) ((szam++)) ==== A let kulcsszó használata ==== #!/bin/bash szam=3 let "szam=szam+1" echo $szam Variációk a let kulcsszó használatára: let "szam=szam+1" let "szam+=1" let "szam++" let szam=szam+1 let szam+=1 let szam++ ==== A bc ==== #!/bin/bash declare -i szam=4 bc <<< "$szam+2" #!/bin/bash declare -i szam=4 echo "$szam+2" | bc ===== Bevitel ===== Ebben a részben megnézzük, hogyan kérhetünk be a billentyűzetről értékeket. echo -n "Szó: " read SZOVEG Egy szöveg bekérése a SZOVEG változóba. A bekérést a read utasítás végzi. Az előtte lévő echo utasítás jelzi a "Szó: " képernyőre való írásával, hogy egy szó begépelését várjuk. Az echo -n kapcsolója a "Szó: " szöveg után nem ír sortörést. Így a bekérés rögtön a kettőspont-szóköz után lehetséges. Bekérés echo nélkül: #!/bin/bash read -p "Felhasználónév: " USER echo "A felhasználónév: "$USER A bekérés szövegét echo nélkül is kitudjuk íratni a read -p kapcsolója segítségével. Beolvasás tömbbe: #!/bin/bash echo -n "Gyümülcsök: " read -a GYUMOLCSOK echo ${GYUMOLCSOK[0]} echo ${GYUMOLCSOK[1]} A read utasítás a -a kapcsoló hatására indexelt tömbváltozót hoz létre. Lásd "help read". A beolvasandó értékeket szóközzel tagolva adjuk meg. Például: Gyümölcsök: körte alma barack szilva Jelszó beolvasása: #!/bin/bash echo -n "Jelszó: " read -s JELSZO echo echo "A beírt jelszó: " $JELSZO #!/bin/bash read -p "Jelszó: " -s JELSZO echo echo "A beírt jelszó: " $JELSZO Alapértelmezett szöveg megadása: #!/bin/bash read -e -i "joska" -p "Felhasználónév: " USER echo "A beírt felhasználónév: "$USER A "-e" kapcsoló is szükséges. ===== Szelekció ===== ==== if ==== #!/bin/bash SZAM=2 if [ $SZAM -eq 2 ] then echo "Egyenlo" else echo "Negy egyenlo" fi Az -eq egyenlőséget vizsgál. Használható még a "=" vagy a "==" karakterek is, viszont ezen operátorok előtt és után kötelező egy vagy több whitespace karakter. #!/bin/bash SZAM=2 if [ $SZAM -ne 2 ] then echo "Nem egyenlő kettővel" else echo "Egyenlő kettővel" fi A -ne azt jelenti nem egyenlő. Használható helyette a != alak is. #!/bin/bash SZAM=2 if [ $SZAM -gt 2 ] then echo "Nagyobb" else echo "Kisebb vagy egyenlő mint kettő" fi #!/bin/bash SZAM=2 if [ $SZAM -lt 2 ] then echo "Kisebb mint kettő" else echo "Nagyobb vagy egyenlő mint kettő" fi #!/bin/bash SZAM=2 if [ $SZAM -le 2 ] then echo "Kisebb vagy egyenlő mint kettő" else echo "Nagyobb mint kettő" fi #!/bin/bash SZAM=2 if [ $SZAM -ge 2 ] then echo "Nagyobb vagy egyenlő" else echo "Kisebb" fi ==== elif ==== #!/bin/bash read -p "Gyümölcs: " fruit if [ "$fruit" = "alma" ]; then echo "Ez alma" elif [ "$fruit" = "körte" ]; then echo "Ez körte" else echo "Nem alma és nem körte" fi ==== select ==== #!/bin/bash select i in "egy" "kettő" "három" do echo Ezt választottad: echo $i done Kiírja az "egy", "kettő", "három" szavakat sorszámokkal bevezetve. Valójában egy ciklus is elindul. Mindig várja, hogy válasszunk egyet. A ciklus a Ctrl + C billentyűkombinációval is. A ciklus persze megszakítható a break utasítással a cikluson belülről is. #!/bin/bash select i in "egy" "kettő" "három" "kilépés" do echo Ezt választottad: echo $i if [[ "$i" = "kilépés" ]] ; then break fi done ==== És, vagy logikai operátorok ==== Több feltétel is megadható, ha megduplázzuk a szögletes zárójeleket: if [[ $num -eq 3 && "$str" == foo ]] then # Utasítások fi Vagy: #!/bin/bash A=0 B=1 if [[ $A -eq 1 || $B -eq 1 ]] then echo "Ok" else echo "Nem ok" fi És: #!/bin/bash A=0 B=1 if [[ $A -eq 1 && $B -eq 1 ]] then echo "Ok" else echo "Nem ok" fi Kevert: #!/bin/bash A=1 B=0 C=1 if [[ ($A -eq 1 || $B -eq 1) && $C -eq 1 ]] then echo "Ok" else echo "Nem ok" fi ===== Szelekció másként ===== ==== Szimpla szelekció ==== #!/bin/bash A=1 [ $A -eq 1 ] && echo "Ok" || echo "Nem ok" ==== Vagy logikai operátor ==== #!/bin/bash A=1 B=1 [ $A -eq 1 ] && [ $B -eq 1 ] && echo "Ok" || echo "Nem ok" ==== És logikai operátor ==== #!/bin/bash A=1 B=1 [ $A -eq 1 ] && [ $B -eq 1 ] && echo "Ok" || echo "Nem ok" ==== Vegyes logikai operátorok ==== #!/bin/bash A=1 B=1 C=0 ([ $A -eq 1 ] || [ $B -eq 1 ]) && [ $C -eq 1 ] && echo "Ok" || echo "Nem ok" ==== Több ágú elágazás ==== echo "[O]lvas" echo "[H]ozzáfűz" echo "[S]zerkeszt" read menupont case "$menupont" in "O" | "o" ) echo "Olvasási műveletek" ;; "H" | "h" ) echo "Hozzáfűzési műveletek" ;; "S" | "s" ) echo "Szerkesztési műveletek" ;; esac ===== Iteráció ===== ==== for ==== Számok 1-től - 10-ig, variációk: for i in `seq 1 10` do echo $i done for i in `seq 1 10`; do echo $i ; done for i in 1 2 3 4 5 6 7 8 9 10 do echo $i done Egy könyvtár tartalmát végig vesszük: for i in `ls /etc` do echo $i done A példában listázzuk az /etc könyvtár tartalmát. Zip fájlok feldolgozása: #!/bin/bash for file in * do if [[ "$file" = *.zip* ]] then echo "$file" fi done #!/bin/bash for (( i=0; i<10; i++ )) do echo "Helló" done #!/bin/bash for (( i=0; i<10; i++ )) do echo $i done ==== while ==== Amíg típusú ciklus (while) #!/bin/bash SZAMLALO=1 while [ $SZAMLALO -lt 11 ] do echo $SZAMLALO let SZAMLALO=SZAMLALO+1 done Számok bekérése nulla végjelig: #!/bin/bash SZAM=1 while [ $SZAM -ne 0 ] do echo -n "Szám: " read SZAM done Számok bekérése nulla végjelig, közben összegzés. #!/bin/bash SZAM=1 SZUM=0 while [ $SZAM -ne 0 ] do echo -n "Szám: " read SZAM let SZUM=$SZUM+$SZAM done echo "Összeg: " $SZUM A let nélkül sztringkét adja össze. Végtelen ciklus létrehozása: while true do #amit csinálunk a végtelen ciklusban done Másik módszer a végtelen ciklusra: while : do #amit csinálunk a végtelen ciklusban done while : do echo -n "Számot: " read SZAM if [ $SZAM -lt 1 ] then exit 0 fi done ==== Fájl olvasása ==== #!/bin/bash while read sor do echo $sor done < gy.txt #!/bin/bash cat gy.txt | while read sor do echo $sor done ==== Könyvtár tartalmának olvasása ==== A .txt kiterjesztésű fájlok olvasása: #!/bin/bash DIR=konyvtar/alkonyvtar cd $DIR find $DIR -name '*.txt' | while read sor do echo $sor done ==== until ==== #!/bin/bash SZAMLALO=1 until [ $SZAMLALO -gt 10 ] do echo $SZAMLALO let SZAMLALO=SZAMLALO+1 done ===== Tömbök ===== Üres tömb: tomb=() Kezdőértéket zárójelek között adhatunk: tomb=( "egy" "kettő" "három") A Bash tömb tartalmazhat számokat és karakterláncokat is: tomb=( 12 "egy" 45 29 "kettő" "három") Hivatkozás egy tömbelemre: echo ${tomb[1]} Ez a második elemet adja vissza, vagyis az indexelés 0-val kezdődik. A tömb elemei így is felvehetők: tomb[0]=egy tomb[1]=kettő tomb[2]=három echo ${tomb[1]} ==== A tömb tulajdonságai ==== #!/bin/bash #Összes elem: echo ${tomb[*]} echo ${tomb[@]} #Összes index: echo ${!tomb[*]} echo ${!tomb[@]} # Elemek száma: echo ${#tomb[*]} echo ${#tomb[@]} echo ${#tomb[0]} # a 0-dik elem hossza ==== A tömb bejárása ==== #!/bin/bash ketogenEtelek=( "Halsaláta" "Zöldségkör" "Húsleves" "Narancs-joghurt" ) for item in ${ketogenEtelek[*]} do echo $item done A tömb bejárása a for in szerkezettel, csak akkor működik, ha nincs szóköz a tömb elemeiben. Ha a tömb elemei szóközöket is tartalmaznak, akkor a következő ciklus alkalmas bejárásra: A "*" karakter helyett használhatunk "@" karaktert is. Szimpla for ciklus használata: #!/bin/bash etelek=( "Halsaláta (uborka, tonhal, tejföl)" "Zöldségkör (tejföl, túró, zöldségek)" "Húsleves" "Narancs joghurt" ) n=${#etelek[@]} for (( i=0; i ==== Tömb műveletek ==== Hozzáfűzés dobasok=(1 5 4) dobasok+=(7 8) echo ${dobasok[@]} Ürítés: dobasok=() Tömb része: echo ${tomb[@]:2:3} A 2-s indextől szeretnénk, 3 darab karaktert. Elem törlése: tomb=(33 49 56 27) unset tomb[1] echo ${tomb[@]} Ha ezek után kiíratjuk a tömb indexeit, ezt kapjuk: echo ${!tomb[@]} 0 2 3 Hiányzik a 1-s index. Egész tömb törlése: unset tomb ==== Deklarált tömb ==== Indexelt tömb: declare -a szamok Asszociatív tömb: declare -A tomb Indexelt tömb feltöltéssel: declare -a szamok szamok+=35 szamok+=47 Asszociatív tömbnek értékadás: declare -A dolgozo #egy érék hozzáadása: dolgozo[fizetes]=3800000 #több érték hozzáadása: dolgozo=([nev]=János [telepules]=Szolnok) echo ${dolgozo[nev]} ===== String ===== ==== Hossz ==== SZOVEG="Nagy János" meret=${#SZOVEG} echo $meret SZOVEG="abc123" HOSSZ=$(printf "%s" "$SZOVEG" | wc -c) echo ${#SZOVEG} echo -n $SZOVEG | wc -m echo -n $SZOVEG | wc -c printf $SZOVEG | wc -m expr length $SZOVEG expr $SZOVEG : '.*' ==== Kisbetű, nagybetű ==== STR="Alma" echo $STR # alma echo ${STR,,} # alma echo ${STR^^} # ALMA A fenti megoldás, csak a 4.x Bash verziótól működik. Használhatjuk a tr parancsot, ami viszont nem viszi a magyar ékezetes karaktereket. STR="Alma" echo "$STR" | tr '[:upper:]' '[:lower:]' Megoldás lehet még az awk használata: echo "ÁRVÍZTŰRŐ" | awk '{print tolower($0)}' echo "Árvíztűrő" | awk '{print toupper($0)}' A Bash megoldás esetén használhatunk egyetlen ^ vagy , karaktert. Ekkor csak az első karakterre vonatkozik a beállítás. STR=árvíztűrő; echo ${STR^} Árvíztűrő Milyen betűk legyenek nagybetűk: STR=árvíztűrő; echo ${STR^^[v,z]} árVíZtűrő ==== Ékezetlenítés ==== Repülő ékezetek, normál ékezet helyett: echo árvíztűrő | iconv -f UTF-8 -t ASCII//TRANSLIT # apt install unaccent $ echo árvíztűrő | unaccent utf-8 arvizturo $ echo árvíztűrő | sed 'y/áíéóöőúüű/aieooouuu/' ==== Tartalmaz-e? ==== #!/bin/bash if [[ $a == *"é"* ]] then echo "Van benne é betű" else echo "Nincs benne é betű" fi ==== String bejárása karakterenként ==== #!/bin/bash a=béla for (( i=0; i<${#a}; i++ )) do echo "${a:$i:1}" done ==== Regex ==== Az [a-z] mint megfelel az angol ábécé betűinek plusz áíóöúü betűknek, de nem felel meg a ő és ű betűknek. #!/bin/bash a=belí if [[ $a =~ ^[a-z]+$ ]] then echo "Megfelel" else echo "Nem felel meg" fi ==== String darabolása ==== #!/bin/bash str="alma:körte:barack:szilva" IFS=':' read -ra tomb <<< "$str" echo ${tomb[0]} ==== Heredoc szintaxis ==== #!/bin/bash date=$(date "+%Y-%m-%d") cat << EOT ========== $date Óra: Téma: Hiányzók: 1 óra: 2 óra: 3 óra: Dolgozat: Hetes: EOT ==== Üres sorok törlése ==== cat fajl.txt | sed '/^$/d' ==== Adott karakterek törlése ==== A következő karakterek törlése: | . ? - , | cat fajl.txt | sed '/[\.?-\,]//g' ===== Visszatérési érték ===== Vizsgáljunk meg egy program végrehajtásának sikerességét. A példában az smbmount parancsot vizsgáljuk a végrehajtás sikerességéről. #!/bin/bash smbmount //server/megosztasnev -o username=joe,password=titok,uid=joe,gid=csoport if [ $? == 0 ] then echo echo "A végrehajtás sikeres" echo else echo echo "Hiba a végrehajtás során" echo fi ===== Karaktersorozat vizsgálatok ===== Operátorok: | = | egyenlő | | != | nem egyenlő | | < | rövidebb | | > | hosszabb | | -z $NEV | a NEV üres | #!/bin/bash ELSO="Kati" MASODIK="Kati" if [ $ELSO = $MASODIK ] then echo Egyezik else echo Nem egyezik fi Üres vagy nem üres a string: #!/bin/bash ELSO="" if [ -z $ELSO ] then echo Üres else echo Nem üres fi Az ASCII táblában előrébb vagy hátrább van-e: #!/bin/bash ELSO="szilva" MASODIK="zalma" if [ $ELSO \> $MASODIK ] then echo "Hátrébb van" else echo "Előrébb van" fi vagy így: #!/bin/bash ELSO="szilva" MASODIK="zalma" if [[ $ELSO > $MASODIK ]] then echo "Hátrébb van" else echo "Előrébb van" fi #!/bin/bash ELSO="s*" # minta egyezés if [[ $ELSO == s* ]] then echo "s-el kezdődik" else echo "Nem s-el kezdődik" fi #!/bin/bash ELSO="s*" # literális egyezés if [[ $ELSO == "s*" ]] then echo "s* van benne" else echo "Nem s* van benne" fi #!/bin/bash ELSO="s*" # literális egyezés if [ "$ELSO" == "s*" ] then echo "s* van benne" else echo "Nem s* van benne" fi ===== Függvények, eljárás használata ===== Függvény: function kiir() { echo "Helló Világ" } kiir Függvény paraméterrel: function kiir() { echo $1 } kiir "Ezt írd ki" Bemenő paraméter és visszatérési érték: #! /bin/bash function dupla() { local res=$(echo "$1 * 2" | bc) echo "$res" } result=$(dupla 3) echo $result Ez a megoldás is működik, de az előbbi korrektebb: #! /bin/bash function dupla() { res=$(echo "$1 * 2" | bc) } dupla 3 echo $res Példa: #!/bin/bash # Rekurzívan az alkönyvtárakban is átnevez minden fájlt kisbetűsre # Jelenleg csak fájlokra működik, könyvtárakra nem. # Latin2-es környezetben működik (1 karakter == 1 byte), UTF-8-ban nem. # UTF-8-as környezetben a tr helyett a sed parancsot használjuk! function files() { for i in * ; do if [ -f "$i" ]; then j=`echo "$i" | tr '[:upper:]' '[:lower:]'` if [ "$i" != "$j" ]; then mv -i "$i" "$j" fi fi done } function dirs() { for i in *; do if [ -d "$i" ]; then cd "$i" dirs files cd .. fi done } dirs files ==== Tömbök ==== Tömb visszatérési érték: function leker_tomb() { tomb=(9 4 2 5) echo ${tomb[@]} } a_tomb=($(leker_tomb)) echo "Összes elem: "${tomb[@]} echo "Mérete: "${#tomb[@]} ===== Változók hatásköre ===== A következő script szemlélteti a változók hatáskörét. A local kulcsóval létrehozott függvényen belüli változó nem látszik csak a függvényben. Az $ERO nevű változó viszont globális, mindenhol látszik. #!/bin/bash ERO="85" function csokkent { local MERTEK=5 echo "Erő csökkentése..." ERO=`expr $ERO - $MERTEK` } echo "Erő: $ERO" csokkent echo "Erő csökkentés után: $ERO" echo "Csökkentés mértéke: $MERTEK" Külső változó felhasználása: #!/bin/bash szam=45 function csinal() { szam=100 echo "Működik" } csinal echo "$e" ===== Fájl és könyvtár tesztek ===== ==== Használható kapcsolók listája ==== * -b filename A fájl speciális blokk? * -c filename A fájl speciális karakterfájl? * -d directoryname A könyvtár létezik? * -e filename A fájl létezik? * -f filename A fájl általános fájl, nem egy könyvtár? * -G filename Ha a fájl létezik, érvényes tulajdonos érvényes csoportazonosító? * -g filename true ha fájl létezik és van set-group-id * -k filename Sticky bit * -L filename Szimbolikus link * -O filename True ha fájl létezik és az felhasználó érvényes azonosító. * -r filename Ellenőrzés, ha a fájl olvasható. * -S filename Ellenőrzés, ha a fájl socket * -s filename Ellenőrzés, ha a fájl nem nonzero méretű * -u filename Ellenőrzés, ha set-ser-id bit be van állítva * -w filename A fájl írhatóságának ellenőrzés * -x filename A fájl futtathatóságának olvasása ==== A fájl létezésének ellenőrzése ==== #!/bin/bash file="adat.txt" if [ -e $file ]; then echo "A fájl létezik" else echo "A fájl nem létezik" fi ==== Első paraméter létezésének vizsgálata ==== #!/bin/bash if [ -z "$1" ]; then echo usage: $0 directory exit fi ==== Könyvtár vizsgálat ==== Példa a könyvtárvizsgálatra: if [ -d "$DIRECTORY" ]; then # Mit tegyünk, ha a $DIRECTORY könyvtár létezik fi A könyvtár neve (esetleg útvonala) a $DIRECTORY változóban találjuk. Ha a könyvtár nem létezik: if [ ! -d "$DIRECTORY" ]; then # Mit tegyünk, ha a $DIRECTORY nem létezik fi ==== Parancs kimenete ==== [ -z "`ls`" ] && echo "Nincs fájl" A [ -z az ls által visszaadott sztring hosszát vizsgálja, hogy az 0 értékű-e. [ -n "`ls`" ] && echo "Van valamilyen fájl" A [ -n az ls által visszaadott sztring hosszát vizsgálja, hogy az nagyobb-e mint 0. ===== Tesztek a test paranccsal ===== Az if utasítást után a test parancs is használható feltételek meghatározására. A következőkben erre látunk példát. test [ ] A két utasítás visszatérhet 0-val (TRUE) vagy 1 (FALSE) értékekkel. #!/bin/bash if test -e /etc/group then echo "A csoportfájl létezik..." >&2 else echo "Valami nem kerek!" >&2 exit 1 fi A test utasítás után a kifejezés egy -e kapcsolóval kezdődik. Azt mondja egy fájl létezését akarjuk tesztelni. Ez után következik, hogy melyik fájlt szeretnénk tesztelni. A fenti utasításnak teljesen megfelel a "[" karakter. Ha megnézzük az /usr/bin könyvtárat, akkor találni fogunk ott egy ilyen futtatható programot. #!/bin/bash if [ -e /etc/group ] then echo "A csoportfájl létezik..." >&2 else echo "Valami nem kerek!" >&2 exit 1 fi ===== Beépített változók ===== | $0 | A script neve | | $1 | paramét1 | | $2 | paramét2 | | $3 | paramét3 | | $4 | paramét4 | | $5 | paramét5 | | $6 | paramét6 | | $7 | paramét7 | | $8 | paramét8 | | $9 | paramét9 | | $# | argumentumok száma | | $@ | az összes argumentum | | $? | az utolsó folyamat kilépési kódja | | $LINENO | a script aktuális sora | | $HOSTNAME | hol fut a script | | $USER | a scriptet futtató felhasználó | | $SECONDS | a futtatás kezdete óta eltelt idő | | $RANDOM | minden hivatkozáskor más véletlen számot ad | ===== Fájlok, könyvtárak neveinek kezelése ===== A következő példákban egy könyvtárban található fájlok vagy alkönyvtárak tartalma helyett, azok nevein hajtunk végre valamit. Jelenleg csak kiíratjuk a nevüket. #!/bin/bash FAJLOK='*.txt' ls $FAJLOK | while read next_file do echo $next_file done #!/bin/bash DIR=/home/utvonal cd $DIR for FILE in * do echo Könyvtárnevek: $FILE done #!/bin/bash DIR=/home/utvonal cd $DIR find $DIR -name '*.txt' | while read filename do echo $filename done ===== Fájlok tartalmának kezelése ===== A következő példa bemutatja hogyan tudjuk olvasni egy fájl tartalmát. while read sor do echo $sor done < gy.txt Ebben a példában is egy fájl tartalmát olvassuk, kicsit másként. cat gy.txt | while read sor do echo $sor done ===== Backup ===== Egyszerű archiválás: #!/bin/bash tar -czf joskadokuarchiv.tar.gz /home/joska/doku Egy összetettebb archiváló: #!/bin/bash DATUM=$(date +%Y%m%d) MENTENDO=/home/andras/bin KIMENET=mentes-joskahome_$DATUM.tar.gz tar -czf $KIMENET $MENTENDO ===== Paraméterek használata ===== #!/bin/bash if [ "$#" == "0" ] then echo "Használat: ./parosit.sh Név1 Név2" exit fi echo "Ennyi nevet írtál: $#" echo $1 és $2 jó választás együtt echo "A paramétereid: $@" ===== Szignálok elkapása (trap) ===== A trap paranccsal szignálokat kaphatunk el. Ha valaki megnyomja például a Ctrl + C, akkor a script befejezése előtt még tehetek valami a "csinald" nevű függvényben. Persze tetszőleges függvény írható a helyére. #!/bin/bash trap csinald INT csinald() { echo Valaki megnyomta a Ctrl + C! Nem kéne } sleep 5 ===== Temp fájlok ===== $ mktemp vmi.XXX vmi.YzM A program kiírja milyen fájlt hoz létre. Az X-ek heléyre véletlen karaktereket helyettesít a program. Lehet hosszabb is: mktemp vmi.XXXXXXXX A következő módon kinyerhető a fájl neve: TEMP=$(mktemp vmi.XXX) Ezek után így használjuk: TEMP=$(mktemp /tmp/temporary-file.XXXXXXXX) echo valami > ${TEMP} ===== Naplózás ===== Ha szeretnénk, hogy a script a /var/log könyvtárba nalózzon, akkor hozzunk ott létre a script számára egy könyvtárat: # mkdir /var/log/sajatScriptNev Naplózás a /var/log/syslog fájlba is történhet a logger nevű parancs segítségével: logger uzenet Megadható a script neve is: logger –t ScriptNev “Helló Világ” ===== Függelék ===== ==== Futtatható fájlok ==== A futtatható fájlok miről ismerjük fel, itt vannak megadva: /proc/sys/fs/binfmt_misc ==== Telepítés ==== apt-get -y install mc >/dev/null 2>&1 aptitude -y install mc >/dev/null 2>&1 ==== Aktuális könyvtár ==== Parancssorból az aktuális könyvtárnév, útvonal nélkül: basename $PWD Másik lehetőség: echo ${PWD##*/} Esetleg így: ACTDIR=$(basname $(pwd)) echo $ACTDIR ===== Példák ===== ==== Másolás időbélyeg alapján ==== Legrégebbi fájl másolása: #!/bin/bash cd /útvonal/könyvtár cp `ls -t1 *.txt | tail -1` /másik/útvonal/könyvtár/ Legújabb fájl mozgatása: #!/bin/bash cd /útvonal/könyvtár cp `ls -t1 *.txt | head -1` /másik/útvonal/könyvtár/ ==== Feltöltés FTP-re ==== cd /útvonal/könyvtár wput ./ ftp://felhasználónév:jelszó@url/könyvtár/ #!/bin/sh USERNAME=jozsi PASSWORD=titok HOST_NAME=localhost LOG=naplo.txt BATCH=id_batchfile echo "user $USERNAME $PASSWORD" > $BATCH echo "bin" >> $BATCH echo "send egy.txt" >> $BATCH echo "send ketto.txt" >> $BATCH echo "exit" >> $BATCH ftp -n -v $HOST_NAME < $BATCH > $LOG rm $BATCH #!/bin/sh USERNAME=jozsi PASSWORD=titok HOST_NAME=localhost LOG=naplo.txt ftp -n -v $HOST_NAME > $LOG < #!/bin/sh USERNAME=jozsi PASSWORD=titok HOST_NAME=localhost LOG=naplo.txt ftp -n -i -v $HOST_NAME > $LOG < Így bekéri a jelszót #!/bin/sh USERNAME=jozsi HOST_NAME=localhost LOG=naplo.txt ftp -n -i -v $HOST_NAME > $LOG < #!/bin/sh USERNAME=jozsi PASSWORD=titok HOST_NAME=localhost LOG=naplo.txt ftp -n -i -v > $LOG < Kicsit más: #!/bin/sh USERNAME=jozsi PASSWORD=titok HOST_NAME=localhost LOG=naplo.txt ftp -n -v $HOST_NAME <> $LOG 2>&1 quote user $USERNAME quote pass $PASSWORD bin prompt off mput *.txt exit EOF ==== Dátum ==== === Dátum darabolása === #!/bin/bash read Y m d H M <<<$(date "+%Y %m %d %H %M") echo Az év: $Y echo A hónap: $m echo A nap: $d echo Az óra: $H echo A perc: $M ==== Naplófájl figyelése ==== STR=elnok tail -f /var/log/syslog | while read -r sor do [[ "$sor" != *$STR* ]] && continue echo "Volt bejegyzés" done exit ==== Visszaszámlálás ==== A script használja az espeak programot. Ha nincs telepítve: apt-get install espeak Kimondva: for i in {"öt", "négy", "három", "kettő", "egy", "zéró"};do espeak -vhu "$i" && sleep 0.5; done Ugyanaz kiírva: for i in {5..1};do echo -n "$i. " && sleep 1; done ==== Lejárt műszakik figyelése ==== Egy fájlban tároljuk melyik járműnek mikor jár le a műszakija. Egy scripttel pedig időnként, például időzítve ellenőrizzük, és hangosan kimondatjuk, ha körülbelül 2 hónap van a műszaki lejártáig. Rendszám:Műszaki lejár:Szükséges figyelmeztetés:Gyártmány:Egyéb ABC-123:2018-08-22:igen:Lada:Nincs ABD-123:2018-08-22:igen:Lada:Nincs BBC-623:2016-04-22::Lada:Nincs CAC-523:2018-02-22:igen:Lada:Nincs ABK-423:2012-04-22:igen:Lada:Nincs AKG-123:2016-05-22:igen:Lada:Nincs GBC-123:2015-08-22:igen:Lada:Nincs #!/bin/bash FILE=vehicles.txt # DEBUG-hoz: FIXDATE=2016-03-03 DATE=`date "+%Y-%m-%d"` echo $DATE HANYHONAPPAL_ELORE=2 function checkVehicle() { sor=$1 LEJAR=`echo $sor | awk -F: '{print $2}'` LEJAR_EV=`echo $LEJAR | awk -F- '{print $1}'` AKTUA_EV=`echo $DATE | awk -F- '{print $1}'` if [[ "$LEJAR_EV" == "$AKTUA_EV" ]] then LEJAR_HO=`echo $LEJAR | awk -F- '{print $2}'` AKTUA_HO=`echo $DATE | awk -F- '{print $2}'` TEMP_HO=`expr $LEJAR_HO - $AKTUA_HO` if [[ $TEMP_HO -le $HANYHONAPPAL_ELORE ]] then LEJAR_RENDSZAM=`echo $sor | awk -F: '{print $1}'` espeak -vhu "Filgyelem! Lejáró műszaki." espeak -vhu "A $LEJAR_RENDSZAM rendszámú gépjármű műszakija ekkor lejár: $LEJAR" fi fi } cat $FILE | while read sor do ERTESITES=`echo $sor | awk -F: '{print $3}'` if [[ "$ERTESITES" == "igen" ]] then checkVehicle $sor fi done ==== A set parancs ==== Sikertelen parancs esetén kilépés: #!/bin/bash # Ha végrehajtás sikertelen a scriptben, akkor azonnal lépjen ki: set -e # Ha az aaaaa fájl nem létezik, akkor kilép. # Ha nincs set -e, akkor még kiírja a Valami szöveget is. ls aaaaa echo Valami Próbáljuk meg a scriptet végrehajtani set -e használata nélkül is. ==== Zónafájl felvétele ==== A Debian GNU/Linux rendszeren a bind démon számára a zónákat a következő helyen kell beállítani: * /etc/bind/named.conf.local Ezek után egy zónafájlt kell készíteni ebbe a könyvtárba: * /var/cache/bind/master Ezt fogjuk automatizálni: #!/bin/bash CONFIG=/etc/bind/named.conf.local MZONES=/var/cache/bind/master echo -n "Tartománynév: " read DOMAIN echo -n "IP cím: " read IP ZONEFILE=$DOMAIN.zone cat << EOT >> $CONFIG zone "$DOMAIN" in { type master; file "master/$ZONE"; }; EOT cat << EOT >> $MZONES/$ZONEFILE \$TTL 3D @ SOA $DOMAIN. hostmaster.$DOMAIN. ( 2017111701 8H 2H 4W 2H) $DOMAIN. NS $DOMAIN. $DOMAIN. A $IP EOT ==== Kártyalapok kiíratása ==== #!/bin/bash function lap() { x=$1 y=$2 f="\u2660" # pikk tput cup $y $x echo -e "\u250c\u2500\u2500\u2510" tput cup $((y+1)) $x echo -e "\u25026$f\u2502" tput cup $((y+2)) $x echo -e "\u2514\u2500\u2500\u2518" } clear lap 5 5 lap 10 5 Az eredmény: ┌──┐ ┌──┐ │6♠│ │6♠│ └──┘ └──┘ A vonalak: u2550 u252c u250c ┌ ─ ┬ ┐ u2510 u2502 │ u251c ├ ┼ u253c ┤ u2524 u2514 └ ┴ ┘ u2518 u2534 * treff (♣) -- u2663 * káró (♦) -- u2666 * kőr (♥) -- u2665 * pikk (♠) -- u2660 * További unicode: https://unicode-table.com/hu/ ===== Külső linkek ===== * http://tldp.org/LDP/Bash-Beginners-Guide/html/ 2018) * http://tldp.org/LDP/abs/html/ (Advanced Bash-Scripting Guide; 2018) * http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html (2018) * http://www.freeos.com/guides/lsst/ch02sec01.html (2018) * http://www.freeos.com/guides/lsst/ (2018) * http://user.it.uu.se/~matkin/documents/shell/#simple_commands (2018) * http://www.arachnoid.com/linux/shell_programming.html (2018) * http://www.inf.u-szeged.hu/~kato/teaching/unix/unix3.html (2018) * http://www.arcania.hu/Informatika/linux/commands.html (2018) * http://www.linuxconfig.org/Bash_scripting_Tutorial (2018) * http://mywiki.wooledge.org/BashPitfalls (2018) * http://wiki.koczka.hu/index.php/Kezd%C5%91lap (Operációs rendszerek; 2018) * http://wiki.bash-hackers.org (2018) * http://wiki.bash-hackers.org/commands/classictest (2018) * http://www.comptechdoc.org/os/linux/programming/script/linux_pgscriptintro.html (2018) * http://www.linuxjournal.com/content/bash-arrays (2018) * http://www.linuxjournal.com/article/2807 (dialog parancs; 2018) * http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html (2018) * http://hup.hu/node/111049 (Hasznos fórum téma; 2018) * http://www.shelldorado.com/scripts/categories.html (2018) * http://www.linuxquestions.org/ (2018) * https://ryanstutorials.net/bash-scripting-tutorial/bash-variables.php (Változók; 2018) * https://www.linuxjournal.com/content/return-values-bash-functions (2020) * https://linuxhint.com/return-string-bash-functions/ (2020) * https://github.com/ruanyf/simple-bash-scripts (2020)