5. gyakorlat

Mi az a shell script?

  • Tulajdonképpen közönséges szövegfileok.
  • Első megközelítésben ugyanazokat a parancsokat tartalmazhatja, amelyekete parancssorból is kiadhatunk
  • Mi kell a futtatáshoz?
    1. "Magic number" (#!/bin/bash)
    2. x jog (vagy: bash script_nev)
    3. Elérési úttal kell futtatni (vagy: export PATH=$PATH:. )

Néhány egyszerű példa

#!/bin/bash # Ez egy megjegyzés echo "Hello világ!" echo "Hello $USER!" echo 'Hello $USER!' echo "Hello ${USER}!" echo 'Hello ${USER}!' echo "Hello $USERalma" echo 'Hello $USERalma' echo "Hello ${USER}alma" echo 'Hello ${USER}alma' exit 0

#!/bin/bash clear echo "Program starts now." echo "Hello, $USER" echo echo "Today's date is `date`" echo echo "These users are currently connected:" who | cut -d' ' -f1 | sort -u echo echo "This is `uname -s` running on a `uname -m` processor." echo echo "This is the uptime information:" uptime echo echo "That's all folks!" exit 0

Változók használata

  • Ugyanúgy használhatjuk a környezeti változókat, mint eddig, azonban pár dolgot szem előtt kell tartanunk
  • Amikor elindítunk egy scriptet, a shell tulajdonképpen lemásolja önmagát, így a gyerekfolyamat ugyanazt a környezetet "látja", mint amit a shell (forkolás)
    		    (bash)
    		      |
    		      |
    		   +--+--+
    		   |     |
    		   |     |
    		(bash) (bash)
    		
  • Ezután a létrejött gyerekfolyamatban betöltődik a mi scriptünk a memóriába
  • Ezt nevezik fork-and-exec mehanizmusnak (egy program lecserélődik egy másikra, miközben a futtató környezet ugyanaz marad)
Mi következik ebből?
  • Ha mi a scriptünkben létrehozunk egy változót, az csak a scripten belül "látszik", hiszen a scriptünk egy külön shellben fut a forkolás miatt, és azt már láttuk, hogy a shellek lokális változói nem látszanak a külvilág felé
    SŐT: Változók használata
  • Megoldás: exportálás, ahogy azt már láttuk
  • MEGJEGYZÉS: A forkolást ki lehet kerülni, ha a következő formában futtatjuk a scriptünket:
    source scriptnev
    . scriptnev

#!/bin/bash szam=43 szoveg1="Hello világ" echo $szam echo $szoveg1 szoveg2=szam echo $szoveg2 szoveg2=$szam echo $szoveg2 exit 0

  • A script futtatása után próbáljuk ki az
    echo $szam
    parancsot, így láthatjuk, hogy a 'szam' változó valóban csak a scripten belül volt elérhető
  • Tegyük meg ugyanezt úgy, hogy .-tal futtatjuk

Speciális bash (belső)változók

$$ A process azonosítója (PID)
$? Az előzőleg futott program státuszkiemente
$# A parancssori paraméterek száma
$0 Az adott héjprogram neve
$1,$2,...,$9,${10}A scriptnek átadott parancssori paraméterek
$* Valamennyi paraméter egyben egy karakterláncként
("$1 $2 ...")
$@ Valamennyi paraméter egyben, de külön idézõjelbe téve
("$1" "$2" ...)
"$@" ""$1" "$2" ..."

#!/bin/bash echo '$# értéke:' $# echo echo '$* értéke:' $* echo '$@ értéke:' $@ echo '"$@" értéke:' "$@" echo echo "Első paraméter: "$1"" echo "Második paraméter: "$2"" echo X="$1""$2" echo "Összefűzve: "$X"" exit 0

shift utasítás

  • A parancssori paramétereket tudjuk léptetni vele
  • Hatására a legelső ($1) parancssori paraméter elveszik a többi ($2,$3,..) pedig eggyel előbbre lép
  • Természetesen a $# értéke is csökken 1-gyel

Az összes parancssori paraméter feldolgozása - variációk egy témára


Klasszikus

for i in $@ do echo $i done

Ötletes

while [ $# -gt 0 ] do echo $1 shift done

Veszélyes - könnyű elrontani

for((i=1;i<=$#;i++)) do echo ${!i} done

Még néhány szó a változókról

#!/bin/bash echo $szam echo ${szam-0} echo $szam echo ${szam=0} #használható ehelyett: echo ${szam:=0} echo $szam unset szam echo ${szam?"hiba"} exit 0

#!/bin/bash szam=3 $szam | masik_program #HIBÁS echo $szam | masik_program szoveg="" cat f1.txt > szoveg #HIBÁS szoveg=`cat f1.txt` exit 0

Debugging - hibakeresés

  • bash -x scriptnev
  • A fenti módon az egész script debuggolva fog futni, azonban van lehetőség arra, hogy a scriptnek csak egy részén kapcsoljuk be a hibakeresét

    set -x # ide jön a debuggolni kívánt rész set +x

    Ekkor simán futtathatjuk a scriptet, nem kell a 'bash -x'

  • Letölthető egy fejeltt debug rendszer a bash-hoz (http://bashdb.sourceforge.net/)
  • Próbáljuk ki:
    bash -x 05_pelda02
    bash -x 05_pelda03

Aritmetikai műveletek

  • http://people.inf.elte.hu/spala/unix_gyak/1/expr1
  • Más lehetőségek
    let X=$X+1
    let X=X+1
    ((X=$X+1))
    ((X=X+1))
    echo $((X+1)
  • Részletesen:
    1. expr
      lásd fenti link
    2. let
      +, -, *, / (egész osztás), %, ++, --
    3. $((EXP))
      tudja azokat, amiket a let, plusz:
      ** (hatványozás)
      <<, >> (shiftelés)
      &, |, ^(=xor), ~ (bitműveletek)
    4. $[EXPR]
      ugyanaz, mint a $((EXPR))
      Ne keverjük össze a test-tel -> ld később

Vezérlési szerkezetek


if elágazás

if TEST-COMMANDS then ..... else .... fi

A test parancs segítségével tudunk logikai feltételeket megfogalmazni, azonban bash-ban a [ ] zárójelpárral egyszerűsíthető az írásmód

		[ -e FILE ]	True if FILE exists.
		[ -d FILE ]	True if FILE exists and is a directory.
		[ -f FILE ]	True if FILE exists and is a regular file.
		[ -h FILE ]	True if FILE exists and is a symbolic link.
		[ -L FILE ]	True if FILE exists and is a symbolic link.
		[ -r FILE ]	True if FILE exists and is readable.
		[ -w FILE ]	True if FILE True if FILE exists and is writable.
		[ -x FILE ]	True if FILE exists and is executable.
		[ -s FILE ]	True if FILE exists and has a size greater than zero.

		[ -z STRING ]	True of the length of "STRING" is zero.
		[ -n STRING ]	True of the length of "STRING" is non-zero.
		[ STRING ]	True of the length of "STRING" is non-zero.
		[ STRING1 == STRING2 ] 	True if the strings are equal.
					"=" may be used instead of "==" for strict POSIX compliance.
		[ STRING1 != STRING2 ] 	True if the strings are not equal.
		[ STRING1 < STRING2 ] 	True if "STRING1" sorts before "STRING2" lexicographically in the current locale.
		[ STRING1 > STRING2 ] 	True if "STRING1" sorts after "STRING2" lexicographically in the current locale.
		[ ARG1 OP ARG2 ]	"OP" is one of:
					-eq (=)
					-ne (!=)
					-lt (<)
					-le (<=)
					-gt (>)
					-ge (>=)
					"ARG1" and "ARG2" are integers.

		[ ! EXPR ]	True if EXPR is false.
		[ ( EXPR ) ]	Returns the value of EXPR. 
				This may be used to override the normal precedence of operators.
		[ EXPR1 -a EXPR2 ]	True if both EXPR1 and EXPR2 are true.
		[ EXPR1 -o EXPR2 ]	True if either EXPR1 or EXPR2 is true.

		-------------------------------------------------------------
		[ -b FILE ]	True if FILE exists and is a block-special file.
		[ -c FILE ]	True if FILE exists and is a character-special file.
		[ -g FILE ]	True if FILE exists and its SGID bit is set.
		[ -k FILE ]	True if FILE exists and its sticky bit is set.
		[ -p FILE ]	True if FILE exists and is a named pipe (FIFO).
		[ -S FILE ]	True if FILE exists and is a socket.
		[ -u FILE ]	True if FILE exists and its SUID (set user ID) bit is set.
		[ -t FD ]	True if file descriptor FD is open and refers to a terminal.
		[ -O FILE ]	True if FILE exists and is owned by the effective user ID.
		[ -G FILE ]	True if FILE exists and is owned by the effective group ID.
		[ -N FILE ]	True if FILE exists and has been modified since it was last read.

		[ FILE1 -nt FILE2 ]	True if FILE1 has been changed more recently than FILE2, 
					or if FILE1 exists and FILE2 does not.
		[ FILE1 -ot FILE2 ]	True if FILE1 is older than FILE2, or is FILE2 exists and FILE1 does not.
		[ FILE1 -ef FILE2 ]	True if FILE1 and FILE2 refer to the same device and inode numbers.
		[ -o OPTIONNAME ]	True if shell option "OPTIONNAME" is enabled.
		-----------------------------------------------------------------
		

#!/bin/bash if [ $# -gt 0 ] then echo "Adtak meg paramétert" if [ "$1" = "-h" -o "$1" = "--help" ] then echo "Segítek!!" exit 0 fi else echo "Nem adtak meg paramétert!" >&2 exit 1 fi echo "Ez mikor fog megjelenni?" exit 0

#!/bin/bash if [ "$(whoami)" != "root" ]; then echo "You have no permission to run $0 as non-root user." >&2 exit 1; fi #ide jön a tényleges kód exit 0

#!/bin/bash # This script gives information about a file. FILENAME="$1" echo "Properties for $FILENAME:" if [ -f $FILENAME ] then echo "Size is $(ls -lh $FILENAME | awk '{ print $5 }')" echo "Type is $(file $FILENAME | cut -d":" -f2)" echo "Inode number is $(ls -i $FILENAME | cut -d" " -f1)" else echo "File does not exist." fi exit 0

Speciális kétirányú elágazás

expr ? expr : expr

X=5 Y=$(($X==0 ? 1 : $X)) echo Y

case elágazás

case EXPRESSION in CASE1) COMMAND-LIST;; CASE2) COMMAND-LIST;; ... CASEN) COMMAND-LIST;; esac

Az egyes ágak feltételeiben a joker helyettesítési szabáloky érvényesek!
case-nél illetve test ([ ])-nél nem használhatók a szabályos kifejezések!

#!/bin/bash if [ $# -eq 0 ] then echo "Adjon meg parametert!" >&2 exit 1 fi case $1 in 0) echo "Nulla";; 1) echo "Egy";; 2) echo "Ketto";; 3) echo "Harom";; 4) echo "Negy";; 5) echo "Ot";; *) echo "Otnel nagyobb szam";; esac exit 0

#!/bin/bash KERDES="Szereted a Linuxot? [i/n] " echo -n $KERDES read VALASZ case $VALASZ in i*|I*) echo "Örülök"; exit 0;; n*|N*) echo "Majd megszereted"; exit 0;; *) echo "Válaszolj Igennel vagy Nemmel!"; exit 1;; esac

for ciklus

for NAME [in LIST ] do COMMANDS done

Alapvetően egy for ciklussal egy lista elemein tudunk végigmenni. Az egyes elemeket egymástól WP-ek választják el egymástól.

#!/bin/bash for i in a b c d e do echo $i done echo for i in a b "c d" e do echo $i done exit 0

#!/bin/bash for i in `cat lista.txt` do echo -n "File: "$i"" cp "$i" "$i".bak echo " copied" done exit 0

while ciklus

while TEST-COMMANDS do COMMANDS done

  • Úgy működik, mint pl a C-ben megismert while ciklus
  • A feltétel megadása úgy történik, mint az if-nél ( test <=> [ ] )

until ciklus

until TEST-COMMANDS do COMMANDS done

  • nem a szokásos hátultesztelős ciklus
  • nem biztos hogy egyszer lefut a ciklusmag
  • kilépési feltételt kell megadni, azaz a ciklus addig fut, amíg a feltétel igaz nem lesz
  • Igazából ugyanaz, mint a while ciklus az egyetlen különbség, hogy a while-nál bentmaradási feltételt kell megadni, itt pedig kilépésit

#!/bin/bash echo "for ciklus:" for i in `seq 10` do echo $i done echo echo "while ciklus:" i=1 while [ $i -le 10 ] do echo $i i=`expr $i + 1` done > szamok.txt echo echo "until ciklus:" i=1 until [ $i -gt 10 ] do echo $i i=`expr $i + 1` done | tee szamok2.txt exit 0

Ami kimaradt eddig


Tömbök

  • Csak 1 dimenzios tömböket használhatunk
  • 0-tól indexelődik, mint C-ben
  • Nincs méretkorlát és nem kell előre megadni, hogy mekkore vektort akarunk használni
  • Ugyanúgy működik, mint a sima változók
  • Létrehozás/Értékadás:
    ARRAY=(value1 value2 ... valueN)
    Ilyenkor a tömb első N elemének értéket adunk, azaz ARRAY[0]-nak, ARRAY[1]-nek,...., ARRAY[N-1]-nek

    ARRAY[NUM]=value
    Ilyenkor egyetlen elemnek adunk értéket
  • Egy tömb összes elemének kiírása:
    echo ${ARRAY[*]}
  • Egy adott elem kiírása:
    echo ${ARRAY[2]}

ARRAY=(one two three) echo ${ARRAY[*]} echo $ARRAY[*] echo ${ARRAY[2]} ARRAY[3]=four echo ${ARRAY[*]} unset ARRAY[1] echo ${ARRAY[*]} unset ARRAY echo ${ARRAY[*]}

Néhány trükk

STR="ABCDEFGH" echo ${#STR} # kiírja hány karakterből áll STR echo ${STR:start:hossz} # részstringet lehet kivágni: # 'start'. karaktertől 'hossz' db karaktert ad vissza # 0-tól kezdődik a poziciók számolása echo ${STR:start} # a 'start'. karaktertől kezdve a string végégig echo ${#ARRAY} # tömb elemszáma echo ${STR/PATTERN/NEWSTRING} # STR-ben a PATTERN-nek megfelelő # mintát cseréli NEWSTRING-re

# Tegyük fel, hogy csináltunk 3 változót: cica1, cica2, cica3 cica1=10 cica2=20 cica3=30 # Hogyan tudjuk ügyesen kiratni (vagy bármire felhasznáni) a változók értékeit? for((i=1;i<4;i++)) do echo $((cica$i)) # Figyelem: az echo $cica$i nem működik! Gondoljátok meg, hogy miért... # Megjegyzés: ugyanúgy jó megoldás az # eval echo \$cica$i # de ez sokkal rondább :) done

Függvények

  • Kétféle szintaktika is jó

    function func_name { .... }

    func_name() { ..... }

  • Úgy működnek mint egy-egy miniscript, kaphatnak paramétereket, és ezeket ugyanúgy $1,$2 stb. formában lehet elérni.
  • Használhatnak saját változókat, melyek csak a fv belsejében "élnek"
  • return utasítással lehet visszatérni egy fv-ből
  • Ha adunk meg vmilyen értéket a return után, akkor a fv statusából lehet azt kiolvasni a visszatérés után

#!/bin/bash Osszead1() { szam1=5 szam2=7 echo $((szam1+szam2)) } Osszead1 szam1=3 Osszead1 exit 0

#!/bin/bash Osszead2() { szam1=$1 # FIGYELEM!! NEM A PARANCSSORI PARAMÉTEREK!! szam2=$2 # HANEM A FV. SAJÁT PARAMÉTEREI!! echo $((szam1+szam2)) } Osszead2 4 5 Osszead2 $1 $2 #parancssori paraméterek exit 0

Gyakorló feladatok

  • Próbáld ki mi történik, ha mást írsz egy script első sorába a #!/bin/bash helyett, próbáld ki azt is, hogy az első sor üres és a másodikban írod a "magic number"-t
  • Írj scriptet, mely pontosan 2 paramétert vár és összeadja a kapott két számot (feltehető, hogy számokat adtak meg)
  • Írj scriptet, mely pontosan 3 paramétert vár, az első kettő egy-egy egész szám, a harmadik pedig egy műveleti jel. A műveleti jel lehet: +,-,*,/,% A script végezze el a két számon a megadott műveletet
  • Írj scriptet, mely segít eldönteni egy rendszergazdának, hogy egy adott usernév létezik-e már a rendszeren vagy nem. A script vagy paraméterként kapja meg a usernevet (pontosan 1 db paraméter) vagy ha nem kapott paramétert, akkor beolvassa a stdin-ről. Ezekután a /etc/passwd állomány segítségével ellenőrizze, hogy létezik-e a megadott usernév, ha igen, akkor írjon ki egy hibaüzenetet, ha még nincs ilyen usernév, akkor írja ki az stdout-ra a megadott usernevet és az "OK" stringet. Ha a scriptet '-h' vagy '--help' kapcsolóval hívják meg, akkor írjon ki pár sor helpet, ebben szerepeljen, hogy mire jó a program, hogyan kell használni és hogy ki készítette a programot (*)
  • Írj scriptet, mely pontosan egy könyvtárat vár paraméterül. Írd ki az öt legnagyobb file-jának a nevét. (*)