Abbiamo introdotto gli array nel capitolo Array. Questo documento mostrerà altri modi per usare gli array.
Nel capitolo precedente sugli array hai visto "${myarray[@]}", con l'indice @, usato per espandere l'array nei singoli elementi.
A volte però vuoi unire tutti gli elementi in un'unica stringa.
Per farlo, usa l'indice *:
echo "${myarray[*]}"
È necessario racchiudere l'espansione tra virgolette doppie.
Bash usa il primo carattere della variabile builtin IFS come carattere separatore.
Per impostazione predefinita, $IFS è composto da spazio, tabulazione e a capo.
myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"
Possiamo modificare la variabile IFS per usare un carattere separatore diverso:
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"
Localizzare IFS nella funzione significa che non dobbiamo salvare il vecchio valore e ripristinarlo al suo valore precedente nello scope globale.
Come affinamento, il parametro speciale "$*", quando è tra virgolette, ha la stessa funzionalità, quindi non serve salvare una copia degli argomenti della funzione:
join() {
local IFS=$1
shift
echo "$*"
}
(IFS=","; echo "${myarray[*]}")
Le parentesi creano una subshell (una copia della shell corrente). Quando i comandi all'interno delle parentesi terminano, la subshell esce e la variabile IFS modificata scompare.
Nota che questo non funzionerà: IFS="," echo "${myarray[*]}", l'espansione dei parametri viene eseguita per prima, prima che la shell applichi la variabile IFS modificata al comando echo.
Potresti aver visto l'espansione dei parametri "${variable:offset:length}" che si espande in una sottostringa del valore della variabile.
Possiamo fare la stessa cosa con gli array per espandere una slice dell'array.
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")
Omettere la parte relativa alla lunghezza significa "dall'offset alla fine dell'array":
subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")
Non è così semplice come in altri linguaggi che potresti conoscere. Ci sono due tecniche principali per passare un array a una funzione.
Nella prima tecnica passi tutti i valori dell'array e li raccogli in un array locale dentro la funzione.
myfunc() {
local array_copy=("$@")
# do stuff with array_copy
declare -p array_copy
}
array_original=(11 22 33 44)
myfunc "${array_original[@]}"
L'array della funzione contiene una copia dei valori. Le modifiche apportate all'array nella funzione non si riflettono nello scope esterno.
Questa tecnica assomiglia di più alla capacità di "passaggio per riferimento" che potresti conoscere da altri linguaggi. Passi il nome dell'array come stringa. La funzione creerà una variabile locale con l'attributo "nameref". Questo array locale e l'array globale (il cui nome abbiamo passato) sono lo stesso array.
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")
I nameref funzionano anche con gli array associativi e con le variabili "scalari" (che contengono un valore di tipo stringa).
All'interno della funzione, declare -p local_array non è molto utile.
Emetterà semplicemente declare -n local_array="array_original".
Puoi ottenere informazioni dettagliate sull'array ispezionando il nome dell'array passato: declare -p "$1"
Fai attenzione che l'array locale abbia un nome diverso dall'array passato. Il codice funzionerà comunque, ma emetterà avvisi come "circular name reference" in questo modo:
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
Nelle shell che mirano a conformarsi solo allo standard POSIX (come ash e dash) non esistono gli array.
La cosa più vicina a cui puoi arrivare è usare i parametri posizionali.
$1, $2, ecc."$@"
"$*"
$#
Usa il comando set per assegnare loro dei valori:
set -- one two three
set -- "$@" four
for item in "$@"; do
echo "do something with $item"
done
Se il tuo obiettivo è scrivere script shell "portabili", userai i parametri posizionali per memorizzare un "array" di valori.