Πολλά σενάρια Bash είναι γραμμένα σε αυστηρά προστακτικό ύφος: εκτέλεσε μια εντολή, μετά εκτέλεσε μια άλλη εντολή, και ούτω καθεξής. Μερικές φορές χρειάζεται να ομαδοποιήσεις μια ακολουθία εντολών που εννοιολογικά εξυπηρετούν έναν και μόνο σκοπό. Εδώ ακριβώς μπαίνουν στο παιχνίδι οι συναρτήσεις.
Ορίζεις μια συνάρτηση ως εξής:
my_function () {
COMMANDS
}
Το κενό ζεύγος παρενθέσεων δηλώνει απλώς ότι ορίζεις μια συνάρτηση. Μέσα τους δεν μπαίνει τίποτα.
Οι συναρτήσεις, αφού οριστούν, συμπεριφέρονται σαν οποιαδήποτε άλλη εντολή (ενσωματωμένη ή όχι).
Όπως με κάθε εντολή, μπορείς να δώσεις ορίσματα στις συναρτήσεις σου.
Μέσα στη συνάρτηση, προσπελαύνεις τα ορίσματα μέσω των θεσιακών παραμέτρων, $1, $2, κ.λπ.
(Θυμήσου, μάθαμε για τις θεσιακές παραμέτρους στην έννοια Μεταβλητές.)
Η ειδική παράμετρος $0 δεν αλλάζει μέσα σε μια συνάρτηση· παραμένει το όνομα του σεναρίου που εκτελείται.
Η συνάρτηση που εκτελείται τη δεδομένη στιγμή μπορεί να προσπελάσει το όνομά της με τη μεταβλητή $FUNCNAME.
Δες την ενότητα 3.4.2 Special Parameters στο εγχειρίδιο.
Μπορείς να ορίσεις μεταβλητές μέσα σε μια συνάρτηση.
Αν δηλώσεις τις μεταβλητές με την εντολή local, η εμβέλεια της μεταβλητής περιορίζεται στην τρέχουσα συνάρτηση (και σε όποιες συναρτήσεις καλεί αυτή).
Διαφορετικά, η μεταβλητή τοποθετείται στην καθολική εμβέλεια.
Οι τοπικές μεταβλητές μπορούν να έχουν το ίδιο όνομα με μια καθολική μεταβλητή. Σε αυτή την περίπτωση, η τοπική μεταβλητή "επισκιάζει" την καθολική μεταβλητή. Για παράδειγμα, μια τοπική μεταβλητή που δηλώνεται μέσα σε μια συνάρτηση κρύβει μια καθολική μεταβλητή με το ίδιο όνομα: οι αναφορές και οι εκχωρήσεις αφορούν την τοπική μεταβλητή, αφήνοντας την καθολική μεταβλητή αμετάβλητη. Όταν η συνάρτηση επιστρέψει, η καθολική μεταβλητή γίνεται και πάλι ορατή.
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"
Αυτό τυπώνει
in the global scope, 5 == 5
in my function, 100 == 100
back in the global scope, 5 == 5
Μέσα σε μια συνάρτηση, μπορείς να προσπελάσεις μεταβλητές από την εμβέλεια αυτού που καλεί. Αυτό σημαίνει ότι μπορείς να χρησιμοποιήσεις καθολικές μεταβλητές, καθώς και τοπικές μεταβλητές που δηλώθηκαν στον καλούντα (ή σε κάποια συνάρτηση που καλεί τον καλούντα).
Τεχνικά, το "καθολικό" δεν είναι η σωστή λέξη. Για να επεκτείνει μια μεταβλητή μέσα σε μια συνάρτηση, το Bash ανεβαίνει τη στοίβα κλήσεων, μέχρι την καθολική εμβέλεια, για να βρει μια συνάρτηση όπου έχει δηλωθεί αυτό το όνομα μεταβλητής.
Αυτό το παράδειγμα είναι προσαρμοσμένο από την ενότητα Shell Functions του εγχειριδίου:
func1() {
local var='func1 local'
func2
}
func2() {
echo "In func2, var = $var"
}
var=global
func1
func2
Η έξοδος είναι:
In func2, var = func1 local
In func2, var = global
Παρομοίως, η εκχώρηση τιμής σε μια μεταβλητή θα την εκχωρήσει στην εμβέλεια όπου δηλώθηκε. Αυτή η "δράση από απόσταση" μπορεί να δημιουργήσει δυσνόητο κώδικα, αφού δεν είναι πάντα προφανές πού εκχωρήθηκε τιμή σε μια μεταβλητή.
Η στοίβα κλήσεων μπορεί να εξεταστεί χρησιμοποιώντας τη μεταβλητή πίνακα FUNCNAME.
Μια συνάρτηση, όπως κάθε εντολή, έχει μια κατάσταση εξόδου. Από προεπιλογή, η κατάσταση μιας συνάρτησης είναι η κατάσταση εξόδου της τελευταίας εντολής που εκτελέστηκε.
Μπορείς να χρησιμοποιήσεις την εντολή return για να επιστρέψεις από μια συνάρτηση με συγκεκριμένη κατάσταση εξόδου.
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
Η χρήση του return χωρίς ορίσματα επιστρέφει την κατάσταση της τελευταίας εντολής που εκτελέστηκε.
Σημείωσε ότι η συνάρτηση check_password μπορεί να απλοποιηθεί ως εξής:
check_password () { [[ $1 == "secret" ]]; }
[[...]] έχει κατάσταση εξόδου: 0 για "αληθής", 1 για "ψευδής".{...} πρέπει να έχει είτε μια αλλαγή γραμμής είτε ένα semicolon πριν το τελικό άγκιστρο.Η κατάσταση επιστροφής μιας συνάρτησης είναι απλώς ένας αριθμός. Πώς μπορεί μια συνάρτηση να παράξει έξοδο;
Η συνάρτηση σου μπορεί να τυπώσει στην τυπική έξοδο. Χρησιμοποίησε τη γνωστή υποκατάσταση εντολής για να την καταγράψεις:
d6 () { echo "$(( 1 + RANDOM % 6 ))"; }
die=$( d6 )
echo "You rolled a $die."
Η κατάσταση εξόδου μιας συνάρτησης είναι διαθέσιμη προς χρήση ακόμη και όταν καταγράφεις την έξοδο.
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
Οι συναρτήσεις μπορούν να καλέσουν τον εαυτό τους αναδρομικά. Από προεπιλογή, δεν υπάρχει όριο στο βάθος της αναδρομής.
Ένα παράδειγμα:
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
Το βάθος της αναδρομής μπορεί να ελεγχθεί με τη μεταβλητή FUNCNEST.
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)