Γνωρίσαμε τους πίνακες στο κεφάλαιο Πίνακες. Αυτό το κείμενο θα δείξει περισσότερους τρόπους χρήσης των πινάκων.
Στο προηγούμενο κεφάλαιο για τους πίνακες, είδες το "${myarray[@]}", με τη θέση @, να χρησιμοποιείται για να επεκτείνει τον πίνακα στα επιμέρους στοιχεία.
Αλλά μερικές φορές θέλεις να ενώσεις όλα τα στοιχεία σε μία μόνο συμβολοσειρά.
Γι’ αυτό, χρησιμοποίησε τη θέση *:
echo "${myarray[*]}"
Είναι απαραίτητο να περικλείσεις την επέκταση σε διπλά εισαγωγικά.
Το Bash χρησιμοποιεί τον πρώτο χαρακτήρα της ενσωματωμένης μεταβλητής IFS ως χαρακτήρα διαχωρισμού.
Από προεπιλογή, το $IFS αποτελείται από διάστημα, tab και αλλαγή γραμμής.
myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"
Μπορούμε να τροποποιήσουμε τη μεταβλητή IFS ώστε να χρησιμοποιήσουμε διαφορετικό χαρακτήρα διαχωρισμού:
myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
join() {
local IFS=$1
shift
local elements=("$@")
echo "${elements[*]}"
}
join ":" "${myarray[@]}" # note, the "@" index
# => "one:two:three:four"
Η τοπικοποίηση του IFS μέσα στη συνάρτηση σημαίνει ότι δεν χρειάζεται να αποθηκεύσουμε την παλιά τιμή και να την επαναφέρουμε στην προηγούμενη τιμή της στο καθολικό πεδίο.
Για μεγαλύτερη βελτίωση, η ειδική παράμετρος "$*", όταν μπει σε εισαγωγικά, έχει την ίδια λειτουργικότητα, οπότε δεν χρειάζεται να αποθηκεύσουμε αντίγραφο των ορισμάτων της συνάρτησης:
join() {
local IFS=$1
shift
echo "$*"
}
(IFS=","; echo "${myarray[*]}")
Οι παρενθέσεις δημιουργούν ένα υποκέλυφος (ένα αντίγραφο του τρέχοντος κελύφους). Όταν ολοκληρωθούν οι εντολές μέσα στις παρενθέσεις, το υποκέλυφος τερματίζεται και η τροποποιημένη μεταβλητή IFS εξαφανίζεται.
Σημείωσε ότι αυτό δεν θα δουλέψει: IFS="," echo "${myarray[*]}". Η επέκταση παραμέτρων εκτελείται πρώτα, πριν το κέλυφος εφαρμόσει την τροποποιημένη μεταβλητή IFS στην εντολή echo.
Μπορεί να έχεις δει την επέκταση παραμέτρων "${variable:offset:length}" που επεκτείνεται σε μια υποσυμβολοσειρά της τιμής της μεταβλητής.
Το ίδιο μπορούμε να κάνουμε και με τους πίνακες, για να επεκτείνουμε ένα τμήμα του πίνακα.
myarray=(one two three four)
subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")
subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")
Αν παραλείψεις το μέρος του μήκους, σημαίνει "από το σημείο εκκίνησης μέχρι το τέλος του πίνακα":
subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")
Αυτό δεν είναι τόσο απλό όσο σε άλλες γλώσσες που μπορεί να ξέρεις. Υπάρχουν δύο βασικές τεχνικές για να περάσεις έναν πίνακα σε μια συνάρτηση.
Στην πρώτη τεχνική, περνάς όλες τις τιμές του πίνακα και τις συγκεντρώνεις σε έναν τοπικό πίνακα μέσα στη συνάρτηση.
myfunc() {
local array_copy=("$@")
# do stuff with array_copy
declare -p array_copy
}
array_original=(11 22 33 44)
myfunc "${array_original[@]}"
Ο πίνακας της συνάρτησης κρατά ένα αντίγραφο των τιμών. Οποιεσδήποτε αλλαγές γίνουν στον πίνακα μέσα στη συνάρτηση δεν αντανακλώνται στο εξωτερικό πεδίο.
Αυτή η τεχνική μοιάζει περισσότερο με τη δυνατότητα "πέρασμα με αναφορά" που μπορεί να ξέρεις από άλλες γλώσσες. Περνάς το όνομα του πίνακα ως συμβολοσειρά. Η συνάρτηση θα δημιουργήσει μια τοπική μεταβλητή με το χαρακτηριστικό "nameref". Αυτός ο τοπικός πίνακας και ο καθολικός πίνακας (του οποίου το όνομα περάσαμε) είναι ο ίδιος πίνακας.
myfunc() {
# note the `-n` option
local -n local_array=$1
# do stuff with local_array
for i in "${!local_array[@]}"; do
printf '%d => %s\n' "$i" "${local_array[i]}"
end
# we can mutate it
local_array+=(55 66 77)
}
array_original=(11 22 33 44)
myfunc "array_original"
# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")
Τα namerefs δουλεύουν επίσης με συσχετιστικούς πίνακες και με μεταβλητές "scalar" (που περιέχουν μια τιμή συμβολοσειράς).
Μέσα στη συνάρτηση, το declare -p local_array δεν είναι ιδιαίτερα χρήσιμο.
Απλώς θα εμφανίσει declare -n local_array="array_original".
Μπορείς να πάρεις λεπτομερείς πληροφορίες για τον πίνακα εξετάζοντας το όνομα του πίνακα που πέρασες: declare -p "$1"
Πρόσεξε ώστε ο τοπικός πίνακας να έχει διαφορετικό όνομα από τον πίνακα που πέρασες. Ο κώδικας θα δουλέψει ακόμα, αλλά θα εμφανίσει προειδοποιήσεις "circular name reference" σαν αυτή:
myfunc() {
local -n a=$1
local IFS=,
echo "${a[*]}"
}
# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three
Σε κελύφη που στοχεύουν να συμμορφώνονται μόνο με το πρότυπο POSIX (όπως το ash και το dash), δεν υπάρχουν πίνακες.
Το πιο κοντινό που μπορείς να κάνεις είναι να χρησιμοποιήσεις τις παραμέτρους θέσης.
$1, $2, κ.λπ."$@"
"$*"
$#
Χρησιμοποίησε την εντολή set για να τους αναθέσεις τιμές:
set -- one two three
set -- "$@" four
for item in "$@"; do
echo "do something with $item"
done
Αν στόχος σου είναι να γράψεις "φορητά" σενάρια κελύφους, θα χρησιμοποιήσεις τις παραμέτρους θέσης για να αποθηκεύσεις μια "λίστα" τιμών.