Sok Bash-szkript szigorúan imperatív stílusban íródik: végrehajt egy parancsot, majd egy másikat, és így tovább. Néha együtt kell kezelned egy olyan parancssorozatot, amely fogalmilag egyetlen célt szolgál. Itt jönnek képbe a függvények.
Egy függvényt így definiálsz:
my_function () {
COMMANDS
}
Az üres zárójelpár egyszerűen azt jelöli, hogy egy függvényt definiálsz. Semmi sem kerül bele.
A függvények a definiálás után úgy viselkednek, mint bármely más parancs (akár beépített, akár nem).
Ahogy bármelyik parancsnak, úgy a függvényeidnek is adhatsz argumentumokat.
A függvényen belül a pozíciós paraméterekkel éred el az argumentumokat: $1, $2 és így tovább.
(Emlékszel, a pozíciós paraméterekről a Változók fogalomnál tanultunk.)
A speciális $0 paraméter egy függvényen belül nem változik meg; továbbra is a futó szkript neve.
Az éppen futó függvény a $FUNCNAME változóból kérdezheti le a saját nevét.
Lásd a kézikönyv 3.4.2 Speciális paraméterek szakaszát.
Egy függvényen belül is definiálhatsz változókat.
Ha a local paranccsal deklarálod őket, a változó scope-ja az aktuális függvényre korlátozódik (és minden olyan függvényre, amelyet az meghív).
Ellenkező esetben a változó a globális scope-ba kerül.
A lokális változók neve megegyezhet egy globális változóéval. Ebben az esetben a lokális változó „árnyékolja” a globális változót. Ha például egy függvényben deklarálsz egy lokális változót, az elrejti az azonos nevű globális változót: a hivatkozások és az értékadások a lokális változóra vonatkoznak, a globális változó pedig változatlan marad. Amikor a függvény visszatér, a globális változó ismét láthatóvá válik.
x=5
myfunc () {
local x=100
echo "in my function, $x == 100"
}
echo "in the global scope, $x == 5"
myfunc
echo "back in the global scope, $x == 5"
Ennek a kimenete
in the global scope, 5 == 5
in my function, 100 == 100
back in the global scope, 5 == 5
Egy függvényen belül hozzáférhetsz a hívó scope-jában lévő változókhoz. Ez azt jelenti, hogy használhatod a globális változókat, valamint azokat a lokális változókat, amelyeket a hívó (vagy a hívót meghívó valamelyik függvény) deklarált.
Szigorúan véve a „globális” nem a legtalálóbb szó. Ahhoz, hogy egy függvényben kifejtsen egy változót, a Bash felmegy a hívási veremen egészen a globális scope-ig, hogy megtalálja azt a függvényt, ahol ezt a változónevet deklarálták.
Ez a példa a kézikönyv Shell-függvények szakaszából származik, kisebb módosításokkal:
func1() {
local var='func1 local'
func2
}
func2() {
echo "In func2, var = $var"
}
var=global
func1
func2
A kimenet:
In func2, var = func1 local
In func2, var = global
Hasonlóképp, ha értéket adsz egy változónak, az abban a scope-ban történik, ahol a változót deklarálták. Ez a „távolba ható művelet” nehezen követhető kódot eredményezhet, mert nem mindig nyilvánvaló, hol kapott értéket egy változó.
A hívási verem a FUNCNAME tömbváltozóval vizsgálható meg.
Egy függvénynek, akárcsak bármely parancsnak, van kilépési státusza. Alapértelmezés szerint a függvény státusza az utolsó végrehajtott parancs kilépési státusza.
A return paranccsal adott kilépési státusszal térhetsz vissza egy függvényből.
check_password () {
if [[ $1 == "secret" ]]; then
return 0
else
return 1
fi
}
read -sp "Enter your password: " pass
if check_password "$pass"; then
echo "Correct!"
else
echo "Wrong password."
fi
Ha a return-t argumentum nélkül használod, az az utolsó végrehajtott parancs státuszát adja vissza.
Vegyük észre, hogy a check_password függvény leegyszerűsíthető így:
check_password () { [[ $1 == "secret" ]]; }
[[...]] feltételes szerkezetnek van kilépési státusza: 0 az „igaz”, 1 a „hamis” esetén.{...} csoportosító szerkezetben a záró kapcsos zárójel előtt vagy egy sortörésnek, vagy egy pontosvesszőnek kell állnia.Egy függvény visszatérési státusza csupán egy szám. Hogyan tud akkor egy függvény kimenetet előállítani?
A függvényed a szabványos kimenetre írhat. A jól ismert parancsbehelyettesítéssel elkaphatod azt:
d6 () { echo "$(( 1 + RANDOM % 6 ))"; }
die=$( d6 )
echo "You rolled a $die."
A függvény kilépési státusza akkor is elérhető, amikor a kimenetét kapod el.
roll () {
local n=$1
if (( 4 <= n && n <= 20 )); then
echo "$(( 1 + RANDOM % n ))" # exit status is 0
else
return 1
fi
}
read -p "How many faces does your die have? " faces
if die=$( roll "$faces" ); then
echo "You rolled a $die."
else
echo "I can't roll a die with $faces faces."
fi
A függvények rekurzívan hívhatják önmagukat. Alapértelmezés szerint a rekurzió mélysége nincs korlátozva.
Egy példa:
fibonacci() {
local n=$1
if (( n <= 1 )); then
echo "1"
else
local a=$(fibonacci "$(( n - 1 ))")
local b=$(fibonacci "$(( n - 2 ))")
echo "$(( a + b ))"
fi
}
for i in {1..10}; do fibonacci "$i"; done
# => 1
# => 2
# => 3
# => 5
# => 8
# => 13
# => 21
# => 34
# => 55
# => 89
A rekurzió mélysége a FUNCNEST változóval szabályozható.
bash -c '
recur() {
echo $1
recur $(($1 + 1))
}
FUNCNEST=5
recur 1
'
1
2
3
4
5
environment: line 1: recur: maximum function nesting level exceeded (5)