Capitolo 26. Array

Le versioni più recenti di Bash supportano gli array monodimensionali. Gli elementi dell'array possono essere inizializzati con la notazione variabile[xx]. In alternativa, uno script può dichiarare un intero array con l'enunciato esplicito declare -a variabile. Per dereferenziare (cercare il contenuto di) un elemento dell'array, si usa la notazione parentesi graffe, vale a dire, ${variabile[xx]}.

Esempio 26-1. Un semplice uso di array

#!/bin/bash


area[11]=23
area[13]=37
area[51]=UFO

#  Non occorre che gli elementi dell'array siano consecutivi o contigui.

#  Alcuni elementi possono rimanere non inizializzati.
#  I "buchi" negli array sono permessi.
#  Infatti, gli array con dati non consecutivi ("array sparsi")
#+ sono utili nei software di gestione dei fogli di calcolo.


echo -n "area[11] = "
echo ${area[11]}    #  sono necessarie le {parentesi graffe}.

echo -n "area[13] = "
echo ${area[13]}

echo "Il contenuto di area[51] è ${area[51]}."

#  Gli elementi non inizializzati vengono visualizzati come spazi
#+ (variabili nulle).
echo -n "area[43] = "
echo ${area[43]}
echo "(area[43] non assegnato)"

echo

# Somma di due elementi dell'array assegnata ad un terzo.
area[5]=`expr ${area[11]} + ${area[13]}`
echo "area[5] = area[11] + area[13]"
echo -n "area[5] = "
echo ${area[5]}

area[6]=`expr ${area[11]} + ${area[51]}`
echo "area[6] = area[11] + area[51]"
echo -n "area[6] = "
echo ${area[6]}
#  Questo assegnamento fallisce perché non è permesso sommare 
#+ un intero con una stringa.

echo; echo; echo

# -----------------------------------------------------------------------------
# Un altro array, "area2".
# Metodo di assegnamento alternativo...
# nome_array=( XXX YYY ZZZ ... )

area2=( zero uno due tre quattro )

echo -n "area2[0] = "
echo ${area2[0]}
#  Aha, indicizzazione in base zero (il primo elemento dell'array 
#+ è [0], non [1]).

echo -n "area2[1] = "
echo ${area2[1]}    # [1] è il secondo elemento dell'array.
# -----------------------------------------------------------------------------

echo; echo; echo

# -----------------------------------------------
# Ancora un altro array, "area3".
# Ed un'altra modalità ancora di assegnamento...
# nome_array=([xx]=XXX [yy]=YYY ...)

area3=([17]=diciassette [24]=ventiquattro)

echo -n "area3[17] = "
echo ${area3[17]}

echo -n "area3[24] = "
echo ${area3[24]}
# -----------------------------------------------

exit 0

Nota

Bash consente le operazioni sugli array anche se questi non sono stati dichiarati tali esplicitamente.

stringa=abcABC123ABCabc
echo ${stringa[@]}               # abcABC123ABCabc
echo ${stringa[*]}               # abcABC123ABCabc
echo ${stringa[0]}               # abcABC123ABCabc
echo ${stringa[1]}               # Nessun output!
                                 # Perché?
echo ${#stringa[@]}              # 1
                                 # Array di un solo elemento.
                                 # La stringa stessa.

# Grazie a Michael Zick per la precisazione.
Una volta ancora questo dimostra che le variabili Bash non sono tipizzate.

Esempio 26-2. Impaginare una poesia

#!/bin/bash
#  poem.sh: Visualizza in modo elegante una delle poesie preferite
#+          dall'autore del documento.

# Righe della poesia (una strofa).
Riga[1]="I do not know which to prefer,"
Riga[2]="The beauty of inflections"
Riga[3]="Or the beauty of innuendoes,"
Riga[4]="The blackbird whistling"
Riga[5]="Or just after."

# Attribuzione.
Attrib[1]=" Wallace Stevens"
Attrib[2]="\"Thirteen Ways of Looking at a Blackbird\""
# La poesia è di Dominio Pubblico (copyright scaduto).

echo

for indice in 1 2 3 4 5    # Cinque righe.
do
  printf "     %s\n" "${Riga[indice]}"
done

for indice in 1 2          # Attribuzione di due righe.
do
  printf "          %s\n" "${Attrib[indice]}"
done

echo

exit 0

# Esercizio:
# ---------
#  Modificate lo script in modo che la poesia da visualizzare
#+ sia presa da un file dati.

Gli array hanno una sintassi loro propria ed anche i comandi e gli operatori standard di Bash posseggono opzioni specifiche adatte per l'uso degli array.

Esempio 26-3. Operazioni diverse sugli array

#!/bin/bash
# array-ops.sh: Un po' di divertimento con gli array.


array=( zero uno due tre quattro cinque )
#Elemento 0   1   2   3     4      5

echo ${array[0]}       #  zero
echo ${array:0}        #  zero
                       #  Espansione di parametro del primo elemento,
                       #+ iniziando dalla posizione nr. 0 (1° carattere).
echo ${array:1}        #  ero
                       #  Espansione di parametro del primo elemento,
                       #+ iniziando dalla posizione nr. 1 (2° carattere).

echo "--------------"

echo ${#array[0]}      #  4
                       #  Lunghezza del primo elemento dell'array.
echo ${#array}         #  4
                       #  Lunghezza del primo elemento dell'array.
                       #  (Notazione alternativa)

echo ${#array[1]}      #  3
                       #  Lunghezza del secondo elemento dell'array.
                       #  Gli array in Bash sono indicizzati in base zero.

echo ${#array[*]}      #  6
                       #  Numero di elementi di array.
echo ${#array[@]}      #  6
                       #  Numero di elementi di array.

echo "--------------"

array2=( [0]="primo elemento" [1]="secondo elemento" [3]="quarto elemento" )

echo ${array2[0]}      # primo elemento
echo ${array2[1]}      # secondo elemento
echo ${array2[2]}      #
                       # Saltato durante l'inizializzazione, quindi nullo.
echo ${array2[3]}      # quarto elemento


exit 0

Con gli array funzionano anche molte delle normali operazioni stringa.

Esempio 26-4. Operazioni sulle stringhe negli array

#!/bin/bash
# array-strops.sh: Operazioni su stringhe negli array.
# Script di Michael Zick.
# Usato con il permesso dell'autore.

#  In generale, qualsiasi operazione stringa nella notazione ${nome ... }
#+ può essere applicata a tutti gli elementi stringa presenti in un array
#+ usando la notazione ${nome[@] ... } o ${nome[*] ...}.


arrayZ=( uno due tre quattro cinque cinque )

echo

# Estrazione di sottostringa successiva
echo ${arrayZ[@]:0}     # uno due tre quattro cinque cinque
                        # Tutti gli elementi.

echo ${arrayZ[@]:1}     # due tre quattro cinque cinque
                        # Tutti gli elementi successivi ad elemento[0].

echo ${arrayZ[@]:1:2}   # due tre
                        # Solo i due elementi successivi ad elemento[0].

echo "-----------------------"

#  Rimozione di sottostringa
#  Rimuove l'occorrenza più breve dalla parte iniziale della(e) stringa(he),
#+ dove sottostringa è un'espressione regolare.

echo ${arrayZ[@]#q*o}   # uno due tre cinque cinque
                        # Controlla tutti gli elementi dell'array.
                        # Verifica "quattro" e lo rimuove.

# L'occorrenza più lunga dalla parte iniziale della(e) stringa(he)
echo ${arrayZ[@]##t*e}  # uno due quattro cinque cinque
                        # Controlla tutti gli elementi dell'array.
                        # Verifica "tre" e lo rimuove.

# L'occorrenza più breve dalla parte finale della(e) stringa(he)
echo ${arrayZ[@]%r*e}   # uno due t quattro cinque cinque
                        # Controlla tutti gli elementi dell'array.
                        # Verifica "re" e lo rimuove.

# L'occorrenza più lunga dalla parte finale della(e) stringa(he)
echo ${arrayZ[@]%%t*e}  # uno due quattro cinque cinque
                        # Controlla tutti gli elementi dell'array.
                        # Verifica "tre" e lo rimuove.

echo "-----------------------"

# Sostituzione di sottostringa

# Rimpiazza la prima occorrenza di sottostringa con il sostituto
echo ${arrayZ[@]/cin/XYZ}   # uno due tre quattro XYZque XYZque
                            # Controlla tutti gli elementi dell'array.

# Sostituzione di tutte le occorrenze di sottostringa
echo ${arrayZ[@]//in/YY}    # uno due tre quattro cYYque cYYque
                            # Controlla tutti gli elementi dell'array.

# Cancellazione di tutte le occorrenze di sottostringa
# Non specificare la sostituzione significa 'cancellare'
echo ${arrayZ[@]//ci/}      # uno due tre quattro nque nque
                            # Controlla tutti gli elementi dell'array.

# Sostituzione delle occorrenze di sottostringa nella parte iniziale
echo ${arrayZ[@]/#ci/XY}    # uno due tre quattro XYnque XYnque
                            # Controlla tutti gli elementi dell'array.

# Sostituzione delle occorrenze di sottostringa nella parte finale
echo ${arrayZ[@]/%ue/ZZ}    # uno dZZ tre quattro cinqZZ cinqZZ
                            # Controlla tutti gli elementi dell'array.

echo ${arrayZ[@]/%o/XX}     # unXX due tre quattrXX cinque cinque
                            # Perché?

echo "-----------------------"


# Prima di passare ad awk (o altro) --
# Ricordate:
#   $( ... ) è la sostituzione di comando.
#   Le funzioni vengono eseguite come sotto-processi.
#   Le funzioni scrivono i propri output allo stdout.
#   L'assegnamento legge lo stdout della funzione.
#   La notazione nome[@] specifica un'operazione "for-each" (per-ogni).

nuovastr() {
    echo -n "!!!"
}

echo ${arrayZ[@]/%e/$(nuovastr)}
# uno du!!! tr!!! quattro cinqu!!! cinqu!!!
# Q.E.D:* L'azione di sostituzione è un 'assegnamento.'

#  Accesso "For-Each"
echo ${arrayZ[@]//*/$(nuovastr argomenti_opzionali)}
#  Ora, se Bash volesse passare semplicemente la stringa verificata come $0
#+ alla funzione da richiamare . . .

echo

exit 0

# * Quod Erat Demonstrandum: come volevasi dimostrare [N.d.T.]

Con la sostituzione di comando è possibile creare i singoli elementi di un array.

Esempio 26-5. Inserire il contenuto di uno script in un array

#!/bin/bash
# script-array.sh: Inserisce questo stesso script in un array.
# Ispirato da una e-mail di Chris Martin (grazie!).

contenuto_script=( $(cat "$0") ) #  Registra il contenuto di questo script ($0)
                                 #+ in un array.

for elemento in $(seq 0 $((${#contenuto_script[@]} - 1)))
  do                #  ${#contenuto_script[@]}
                    #+ fornisce il numero degli elementi di un array.
                    #
                    #  Domanda:
                    #  Perché è necessario  seq 0?
                    #  Provate a cambiarlo con seq 1.
  echo -n "${contenuto_script[$elemento]}"
                    # Elenca tutti i campi dello script su una sola riga.
  echo -n " -- "    # Usa " -- " come separatore di campo.
done

echo

exit 0

# Esercizio:
# ---------
#  Modificate lo script in modo che venga visualizzato
#+ nella sua forma originale,
#+ completa di spazi, interruzioni di riga, ecc.

Nel contesto degli array, alcuni builtin di Bash assumono un significato leggermente diverso. Per esempio, unset cancella gli elementi dell'array o anche un intero array.

Esempio 26-6. Alcune proprietà particolari degli array

#!/bin/bash

declare -a colori
#  Tutti i comandi successivi presenti nello script tratteranno
#+ la variabile "colori" come un array.

echo "Inserisci i tuoi colori preferiti (ognuno separato da uno spazio)."

read -a colori    #  Inserite almeno 3 colori per mettere alla prova le 
                  #+ funzionalità che seguono.
#  Opzione speciale del comando 'read',
#+ che consente l'assegnamento degli elementi di un array.

echo

conta_elementi=${#colori[@]}
# Sintassi speciale per ricavare il numero di elementi di un array.
#     conta_elementi=${#colori[*]} anche in questo modo.
#
#  La variabile "@" permette la suddivisione delle parole, anche se all'interno
#+ degli apici (estrae le variabili separate da spazi).

#  Corrisponde al comportamento di "$@" e "$*"
#+ nei parametri posizionali.

indice=0

while [ "$indice" -lt "$conta_elementi" ]
do    # Elenca tutti gli elementi dell' array.;
  echo ${colori[$indice]}
  let "indice = $indice + 1"
  # Oppure:
  #    indice+=1
  # se eseguito su Bash versione 3.1 o successive.
done
# Ogni elemento dell'array viene visualizzato su una riga singola.
# Se non vi piace, utilizzate  echo -n "${colori[$indice]} "
#
# La stessa cosa utilizzando un ciclo "for":
#   for i in "${colori[@]}"
#   do
#     echo "$i"
#   done
# (Grazie, S.C.)

echo

#  Ancora, elenco di tutti gli elementi dell'array utilizzando, però, un 
#+ metodo più elegante.
  echo ${colori[@]}         # anche echo ${colori[*]}.

echo

# Il comando "unset" cancella gli elementi di un array, o l'intero array.
unset colori[1]             # Cancella il secondo elemento dell' array.
                            # Stesso effetto di colori[1]=
echo  ${colori[@]}          # Elenca ancora l'array. Manca il secondo elemento.

unset colori                # Cancella l'intero array.
                            #  Anche: unset colori[*] e
                            #+ unset colori[@].
echo; echo -n "Colori cancellati."
echo ${colori[@]}           # Visualizza ancora l'array, ora vuoto.

exit 0

Come si è visto nell'esempio precedente, sia ${nome_array[@]} che ${nome_array[*]} fanno riferimento a tutti gli elementi dell'array. Allo stesso modo, per ottenere il numero degli elementi di un array si usa sia ${#nome_array[@]} che ${#nome_array[*]}. ${#nome_array} fornisce la lunghezza (numero di caratteri) di ${nome_array[0]}, il primo elemento dell'array.

Esempio 26-7. Array vuoti ed elementi vuoti

#!/bin/bash
# empty-array.sh

#  Grazie a Stephane Chazelas, per l'esempio originario,
#+ e a Michael Zick e Omair Eshkenazi per averlo ampliato.


# Un array vuoto non è la stessa cosa di un array composto da elementi vuoti.

array0=( primo secondo terzo )
array1=( '' )   # "array1" contiene un elemento vuoto.
array2=( )      # Nessun elemento . . . "array2" è vuoto.
array3=(   )    # E a proposito di questo array?

echo
ElencaArray ()
{
echo
echo "Elementi in array0:  ${array0[@]}"
echo "Elementi in array1:  ${array1[@]}"
echo "Elementi in array2:  ${array2[@]}"
echo "Elementi in array3:  ${array3[@]}"
echo
echo "Lunghezza del primo elemento di array0 = ${#array0}"
echo "Lunghezza del primo elemento di array1 = ${#array1}"
echo "Lunghezza del primo elemento di array2 = ${#array2}"
echo "Lunghezza del primo elemento di array3 = ${#array3}"
echo
echo "Numero di elementi di array0 = ${#array0[*]}"  # 3
echo "Numero di elementi di array1 = ${#array1[*]}"  # 1  (Sorpresa!)
echo "Numero di elementi di array2 = ${#array2[*]}"  # 0
echo "Numero di elementi di array3 = ${#array3[*]}"  # 0
}

# ===================================================================

ElencaArray

# Proviamo ad incrementare gli array

# Aggiunta di un elemento ad un array.
array0=( "${array0[@]}" "nuovo1" )
array1=( "${array1[@]}" "nuovo1" )
array2=( "${array2[@]}" "nuovo1" )
array3=( "${array3[@]}" "nuovo1" )

ElencaArray

# oppure
array0[${#array0[*]}]="nuovo2"
array1[${#array1[*]}]="nuovo2"
array2[${#array2[*]}]="nuovo2"
array3[${#array3[*]}]="nuovo2"

ElencaArray

# Quando sono modificati in questo modo, gli array sono come degli 'stack'
# L'operazione precedente rappresenta un 'push'
# L''altezza' dello stack è:
altezza=${#array2[@]}
echo
echo "Altezza dello stack array2 = $altezza"

# Il 'pop' è:
unset array2[${#array2[@]}-1]  #  Gli array hanno indici in base zero
altezza=${#array2[@]}          #+ vale a dire che il primo elemento ha indice 0
echo
echo "POP"
echo "Nuova altezza dello stack array2 = $altezza"

ElencaArray

# Elenca solo gli elemnti 2do e 3zo dell'array0
da=1            # Numerazione in base zero
a=2 
array3=( ${array0[@]:1:2} )
echo
echo "Elementi dell'array3:  ${array3[@]}"

# Funziona come una stringa (array di caratteri)
# Provate qualche altro tipo di "stringa"

# Sostituzione:
array4=( ${array0[@]/secondo/2do} )
echo
echo "Elementi dell'array4:  ${array4[@]}"

# Sostituzione di ogni occorrenza della stringa con il carattere jolly
array5=( ${array0[@]//nuovo?/vecchio} )
echo
echo "Elementi dell'array5:  ${array5[@]}"

# Proprio quando stavate per prenderci la mano . . .
array6=( ${array0[@]#*nuovo} )
echo # Questo potrebbe sorprendervi.
echo "Elementi dell'array6:  ${array6[@]}"

array7=( ${array0[@]#nuovo1} )
echo # Dopo l'array6 questo non dovrebbe più stupirvi.
echo "Elementi dell'array7:  ${array7[@]}"

# Che assomiglia moltissimo a . . .
array8=( ${array0[@]/nuovo1/} )
echo
echo "Elementi dell'array8:  ${array8[@]}"

#  Quindi, cosa possiamo dire a questo proposito?

#  Le operazioni stringa vengono eseguite su ognuno
#+ degli elementi presenti in var[@] in sequenza.
#  Quindi : Bash supporta le operazioni su vettore stringa.
#+ Se il risultato è una stringa di lunghezza zero,
#+ quell'elemento scompare dall'assegnamento risultante.

#  Domanda: queste stringhe vanno usate con il quoting forte o debole?

zap='nuovo*'
array9=( ${array0[@]/$zap/} )
echo
echo "Elementi dell'array9:  ${array9[@]}"

# Proprio quando pensavate di essere a cavallo . . .
array10=( ${array0[@]#$zap} )
echo
echo "Elementi dell'array10:  ${array10[@]}"

# Confrontate array7 con array10.
# Confrontate array8 con array9.

# Risposta: con il quoting debole.

exit 0

La relazione tra ${nome_array[@]} e ${nome_array[*]} è analoga a quella tra $@ e $*. Questa potente notazione degli array ha molteplici impieghi.

# Copiare un array.
array2=( "${array1[@]}" )
# oppure
array2="${array1[@]}"
#
#  Questo, tuttavia, fallisce con gli array "sparsi",
#+ quelli contenenti dei "buchi" (elementi mancanti),
#+ come ha evidenziato Jochen DeSmet.
# -------------------------------------------------------
array1[0]=0
# array1[1] non assegnato
array1[2]=2
array2=( "${array1[@]}" )       # Lo copierà?

echo ${array2[0]}      # 0
echo ${array2[2]}      # (nullo), avrebbe dovuto essere 2
# -------------------------------------------------------



# Aggiunta di un elemento ad un array.
array=( "${array[@]}" "nuovo elemento" )
# oppure
array[${#array[*]}]="nuovo elemento"

# Grazie, S.C.

Suggerimento

L'operazione di inizializzazione array=( elemento1 elemento2 ... elementoN), con l'aiuto della sostituzione di comando, permette di inserire in un array il contenuto di un file di testo.

#!/bin/bash

nomefile=file_esempio

#            cat file_esempio
#
#            1 a b c
#            2 d e fg


declare -a array1

array1=( `cat "$nomefile" | tr '\n' ' '`)  #  Carica il contenuto
#         Visualizza il file allo stdout   #+ di $nomefile in array1.
#
#  array1=( `cat "$nomefine" | tr '\n' ' '`)
#                            cambia i ritorni a capo presenti nel file in spazi.
#  Non necessario perché Bash effettua la suddivisione delle parole
#+ che modifica i ritorni a capo in spazi.

echo ${array1[@]}            # Visualizza il contenuto dell'array.
#                              1 a b c 2 d e fg
#
#  Ogni "parola" separata da spazi presente nel file
#+ è stata assegnata ad un elemento dell'array.

conta_elementi=${#array1[*]}
echo $conta_elementi         # 8

Uno scripting intelligente consente di aggiungere ulteriori operazioni sugli array.

Esempio 26-8. Inizializzare gli array

#! /bin/bash
# array-assign.bash

#  Le operazioni degli array sono specifiche di Bash,
#+ quindi il nome dello script deve avere il suffisso ".bash".

# Copyright (c) Michael S. Zick, 2003, Tutti i diritti riservati.
# Licenza: Uso illimitato in qualsiasi forma e per qualsiasi scopo.
# Versione: $ID$

# Chiarimenti e commenti aggiuntivi di William Park.

#  Basato su un esempio fornito da Stephane Chazelas,
#+ apparso nel libro: Guida avanzata di scripting bash.

# Formato dell'output del comando 'times':
# CPU Utente <spazio> CPU Sistema
# CPU utente di tutti i processi <spazio> CPU sistema di tutti i processi

#  Bash possiede due modi per assegnare tutti gli elementi di un array
#+ ad un nuovo array.
#  Nelle versioni Bash 2.04, 2.05a e 2.05b.
#+ entrambi i metodi inseriscono gli elementi 'nulli'
#  Alle versioni più recenti può aggiungersi un ulteriore assegnamento
#+ purché, per tutti gli array, sia mantenuta la relazione [indice]=valore.

#  Crea un array di grandi dimensioni utilizzando un comando interno,
#+ ma andrà bene qualsiasi cosa che permetta di creare un array
#+ di diverse migliaia di elementi.

declare -a grandePrimo=( /dev/* )
echo
echo 'Condizioni: Senza quoting, IFS preimpostato, Tutti gli elementi'
echo "Il numero di elementi dell'array è ${#grandePrimo[@]}"

# set -vx



echo
echo '- - verifica: =( ${array[@]} ) - -'
times
declare -a grandeSecondo=( ${grandePrimo[@]} )
#                        ^                   ^
times

echo
echo '- - verifica: =${array[@]} - -'
times
declare -a grandeTerzo=${grandePrimo[@]}
# Questa volta niente parentesi.
times

#  Il confronto dei risultati dimostra che la seconda forma, evidenziata
#+ da Stephane Chazelas, è da tre a quattro volte più veloce.
#
#  Spiega William Park:
#+ L'array grandeSecondo viene inizializzato come stringa singola,
#+ mentre grandeTerzo viene inizializzato elemento per elemento.
#  Quindi, in sostanza, abbiamo:
#                  grandeSecondo=( [0]="... ... ..." )
#                  grandeTerzo=( [0]="..." [1]="..." [2]="..." ... )


#  Nei miei esempi esplicativi, continuerò ad utilizzare la prima forma
#+ perché penso serva ad illustrare meglio quello che avviene.

#  In realtà, porzioni di codice di miei esempi possono contenere
#+ la seconda forma quando è necessario velocizzare l'esecuzione.

# MSZ: Scusate le precedenti sviste.

#  Nota:
#  ----
#  Gli enunciati "declare -a" alle righe 31 e 43
#+ non sarebbero strettamente necessari perch´ sono impliciti
#+ nell'assegnamento nella forma  Array=( ... ).
#  Tuttavia, l'eliminazione di queste dichiarazioni rallenta
#+ l'esecuzione delle successive sezioni dello script.
#  Provate e vedete cosa succede.

exit 0

Nota

L'aggiunta del superfluo enunciato declare -a nella dichiarazione di un array può velocizzare l'esecuzione delle successive operazioni sullo stesso array.

Esempio 26-9. Copiare e concatenare array

#! /bin/bash
# CopyArray.sh
#
# Script di Michael Zick.
# Usato con il permesso dell'autore.

#  Come "Passare per Nome & restituire per Nome"
#+ ovvero "Costruirsi il proprio enunciato di assegnamento".


CpArray_Mac() {

# Costruttore dell'enunciato di assegnamento

    echo -n 'eval '
    echo -n "$2"                    # Nome di destinazione
    echo -n '=( ${'
    echo -n "$1"                    # Nome di origine
    echo -n '[@]} )'

# Si sarebbe potuto fare con un solo comando.
# E' solo una questione di stile.
}

declare -f CopiaArray               # Funzione "Puntatore"
CopiaArray=CpArray_Mac              # Costruttore dell'ennuciato

Enfatizza()
{

# Enfatizza l'array di nome $1.
# (Lo sposa all'array contenente "veramente fantastico".)
# Risultato nell'array di nome $2.

    local -a TMP
    local -a esagerato=( veramente fantastico )

    $($CopiaArray $1 TMP)
    TMP=( ${TMP[@]} ${esagerato[@]} )
    $($CopiaArray TMP $2)
}

declare -a prima=( Lo scripting di Bash avanzato )
declare -a dopo

echo "Array iniziale = ${prima[@]}"

Enfatizza prima dopo

echo "Array finale = ${dopo[@]}"

# Troppo esagerato?

echo "Cos'è ${dopo[@]:4:2}?"

declare -a modesto=( ${dopo[@]:0:2} "è" ${dopo[@]:4:2} )
#                     - estrazione di sottostringhe -

echo "Array modesto = ${modesto[@]}"

# Cos'è successo a 'prima' ?

echo "Array iniziale = ${prima[@]}"

exit 0

Esempio 26-10. Ancora sul concatenamento di array

#! /bin/bash
# array-append.bash

# Copyright (c) Michael S. Zick, 2003, Tutti i diritti riservati.
# Licenza: Uso illimitato in qualsiasi forma e per qualsiasi scopo.
# Versione: $ID$
#
# Impaginazione leggermente modificata da M.C.


# Le operazioni degli array sono specifiche di Bash.
# La /bin/sh originaria UNIX non ne possiede di equivalenti.


#  Collagate con una pipe l'output dello script a 'more'
#+ in modo che non scorra completamente sullo schermo.


# Inizializzazione abbreviata.
declare -a array1=( zero1 uno1 due1 )
# Inizializzazione dettagliata ([1] non viene definito).
declare -a array2=( [0]=zero2 [2]=due2 [3]=tre2 )

echo
echo "- Conferma che l'array è stato inizializzato per singolo elemento. -"
echo "Numero di elementi: 4"        # Codificato a scopo illustrativo.
for (( i = 0 ; i < 4 ; i++ ))
do
    echo "Elemento [$i]: ${array2[$i]}"
done
# Vedi anche il codice d'esempio più generale in basics-reviewed.bash.


declare -a dest

# Combina (accodando) i due array in un terzo.
echo
echo 'Condizioni: Senza quoting, IFS preimpostato, operatore Array-intero'
echo '- Elementi non definiti assenti, indici non mantenuti. -'
# Gli elementi non definiti non esistono; non vengono inseriti.

dest=( ${array1[@]} ${array2[@]} )
# dest=${array1[@]}${array2[@]}     # Risultati strani, probabilmente un bug.

# Ora visualizziamo il risultato.
echo
echo "- - Verifica dell'accodamento dell'array - -"
cnt=${#dest[@]}

echo "Numero di elementi: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Elemento [$i]: ${dest[$i]}"
done

# (Doppio) Assegnamento di un intero array ad un elemento di un altro array.
dest[0]=${array1[@]}
dest[1]=${array2[@]}

# Visualizzazione del risultato.
echo
echo "- - Verifica dell'array modificato - -"
cnt=${#dest[@]}

echo "Numero di elementi: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Elemento [$i]: ${dest[$i]}"
done

# Esame del secondo elemento modificato.
echo
echo '- - Riassegnazione e visualizzazione del secondo elemento - -'

declare -a subArray=${dest[1]}
cnt=${#subArray[@]}

echo "Numero di elementi: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Elemento [$i]: ${subArray[$i]}"
done

#  L'assegnamento di un intero array ad un singolo elemento
#+ di un altro array, utilizzando la notazione '=${ ... }',
#+ ha trasformato l'array da assegnare in una stringa,
#+ con gli elementi separati da uno spazio (il primo carattere di IFS).

# Se gli elementi d'origine non avessero contenuto degli spazi . . .
# Se l'array d'origine non fosse stato inizializzato in modo dettagliato . . .
# Allora come risultato si sarebbe ottenuto la struttura dell'array d'origine.

# Ripristino con il secondo elemento modificato.
echo
echo "- - Visualizzazione dell'elemento ripristinato - -"

declare -a subArray=( ${dest[1]} )
cnt=${#subArray[@]}

echo "Numero di elementi: $cnt"
for (( i = 0 ; i < cnt ; i++ ))
do
    echo "Elemento [$i]: ${subArray[$i]}"
done
echo '- - Non fate affidamento su questo comportamento. - -'
echo '- - Potrebbe divergere nelle versioni di Bash - -'
echo '- - precedenti alla 2.05b - -'

# MSZ: Mi scuso per qualsiasi confusa spiegazione fatta in precedenza.

exit 0

--

Gli array consentono la riscrittura, in forma di script di shell, di vecchi e familiari algoritmi. Se questa sia necessariamente una buona idea, è lasciato al lettore giudicare.

Esempio 26-11. Una vecchia conoscenza: il Bubble Sort

#!/bin/bash
# bubble.sh: Ordinamento a bolle.

# Ricordo l'algoritmo dell'ordinamento a bolle. In questa particolare versione..

#  Ad ogni passaggio successivo lungo l'array che deve essere ordinato,
#+ vengono confrontati due elementi adiacenti e scambiati se non ordinati.
#  Al termine del primo passaggio, l'elemento "più pesante" è sprofondato 
#+ nell'ultima posizione dell'array. Al termine del secondo passaggio, il 
#+ rimanente elemento "più pesante" si trova al penultimo posto. E così via. 
#+ Questo significa che ogni successivo passaggio deve attraversare una 
#+ porzione minore di array. Noterete, quindi, un aumento della velocità 
#+ di visualizzazione dopo ogni passaggio.  


scambio()
{
  # Scambia due membri dell'array.
  local temp=${Paesi[$1]} #  Variabile per la memorizzazione temporanea
                          #+ dell'elemento che deve essere scambiato.
  Paesi[$1]=${Paesi[$2]}
  Paesi[$2]=$temp
  
  return
}  

declare -a Paesi  #  Dichiara l'array,
                  #+ in questo caso facoltativo perché viene inizializzato
                  #+ successivamente.

#  È consentito suddividere l'inizializzazione di un array su più righe
#+ utilizzando il carattere di escape (\)?
#  Sì.

Paesi=(Olanda Ucraina Zaire Turchia Russia Yemen Siria \
Brasile Argentina Nicaragua Giappone Messico Venezuela Grecia Inghilterra \
Israele Peru Canada Oman Danimarca Galles Francia Kenya \
Xanadu Qatar Liechtenstein Ungheria)

# "Xanadu" è il luogo mitico dove, secondo Coleridge,
#+"Kubla Khan fece un duomo di delizia fabbricare".


clear                  # Pulisce lo schermo prima di iniziare l'elaborazione.

echo "0: ${Paesi[*]}"  # Elenca l'intero array al passaggio 0.

numero_di_elementi=${#Paesi[@]}
let "confronti = $numero_di_elementi - 1"

conto=1 # Numero di passaggi.

while [ "$confronti" -gt 0 ]                # Inizio del ciclo esterno
do

  indice=0  # L'indice viene azzerato all'inizio di ogni passaggio.

  while [ "$indice" -lt "$confronti" ]      # Inizio del ciclo interno
  do
    if [ ${Paesi[$indice]} \> ${Paesi[`expr $indice + 1`]} ]
    #  Se non ordinato...
    #  Ricordo che \> è l'operatore di confronto ASCII
    #+ usato all'interno delle parantesi quadre singole.

    #  if [[ ${Paesi[$indice]} > ${Paesi[`expr $indice + 1`]} ]]
    #+ anche in questa forma.
    then
        scambio $indice `expr $indice + 1`  # Scambio.
    fi
    let "indice += 1"  # Oppure, indice +=1 su Bash ver. 3.1 o successive. 
  done # Fine del ciclo interno
  

# ----------------------------------------------------------------------
#  Paulo Marcel Coelho Aragao suggerisce una pi semplice alternativa
#+ utilizzando i cicli for.
#
# for (( ultimo = $numero_di_elementi - 1 ; ultimo > 0 ; ultimo-- ))
##                     Corretto da C.Y. Hunt         ^   (Grazie!)
# do
#     for (( i = 0 ; i < ultimo ; i++ ))
#     do
#         [[ "${Paesi[$i]}" > "${Paesi[$((i+1))]}" ]] \
#             && scambio $i $((i+1))
#     done
# done
# ----------------------------------------------------------------------


let "confronti -= 1" #  Poiché l'elemento "più pesante" si è depositato in 
                     #+ fondo, è necessario un confronto in meno ad ogni
                     #+ passaggio.

echo
echo "$conto: ${Paesi[@]}"  #  Visualizza la situazione dell'array al termine 
                            #+ di ogni passaggio.
echo
let "conto += 1"            # Incrementa il conteggio dei passaggi.

done                        # Fine del ciclo esterno
                            # Completato.

exit 0

--

È possibile annidare degli array in altri array?

#!/bin/bash
#  Array "annidato".

#  Esempio fornito da Michael Zick,
#+ con correzioni e chiarimenti di William Park.

UnArray=( $(ls --inode --ignore-backups --almost-all \
        --directory --full-time --color=none --time=status \
        --sort=time -l ${PWD} ) )  # Comandi e opzioni.

# Gli spazi sono significativi . . . quindi non si deve usare il quoting.

SubArray=( ${UnArray[@]:11:1}  ${UnArray[@]:6:5} )
#  Questo array è formato da sei elementi:
#+     SubArray=( [0]=${UnArray[11]} [1]=${UnArray[6]} [2]=${UnArray[7]}
#      [3]=${UnArray[8]} [4]=${UnArray[9]} [5]=${UnArray[10]} )
#
#  In Bash gli array sono liste collegate (circolarmente)
#+ del tipo stringa (char *).
#  Quindi, non si tratta veramente di un array annidato,
#+ è il suo comportamento che è simile.

echo "Directory corrente e data dell'ultima modifica:"
echo "${SubArray[@]}"

exit 0

--

Gli array annidati in combinazione con la referenziazione indiretta creano affascinanti possibilità

Esempio 26-12. Array annidati e referenziazioni indirette

#!/bin/bash
# embedded-arrays.sh
# Array annidati e referenziazione indiretta.

# Script di Dennis Leeuw.
# Usato con il permesso dell'autore.
# Modificato dall'autore di questo documento.


ARRAY1=(
        VAR1_1=valore11
        VAR1_2=valore12
        VAR1_3=valore13
)

ARRAY2=(
        VARIABILE="test"
        STRINGA="VAR1=valore1 VAR2=valore2 VAR3=valore3"
        ARRAY21=${ARRAY1[*]}
)       # L'ARRAY1 viene inserito in questo secondo array.

function visualizza () {
        PREC_IFS="$IFS"
        IFS=$'\n'       #  Per visualizzare ogni elemento dell'array
                        #+ su una riga diversa.
        TEST1="ARRAY2[*]"
        local ${!TEST1} # Provate a vedere cosa succede cancellando questa riga.
        #  Referenziazione indiretta.
        #  Questo rende i componenti di $TEST1
        #+ accessibili alla funzione.


        #  A questo punto, vediamo cosa abbiamo fatto.
        echo
        echo "\$TEST1 = $TEST1"       #  Solo il nome della variabile.
        echo; echo
        echo "{\$TEST1} = ${!TEST1}"  #  Contenuto della variabile.
                                      #  Questo è ciò che fa la
                                      #+ referenziazione indiretta.
        echo
        echo "-------------------------------------------"; echo
        echo


        # Visualizza la variabile
        echo "Variabile VARIABILE: $VARIABILE"
	
        # Visualizza un elemento stringa
        IFS="$PREC_IFS"
        TEST2="STRINGA[*]"
        local ${!TEST2}      # Referenziazione indiretta (come prima).
        echo "Elemento stringa VAR2: $VAR2 da STRINGA"

        # Visualizza un elemento dell'array
        TEST2="ARRAY21[*]"
        local ${!TEST2}      # Referenziazione indiretta (come prima).
        echo "Elemento VAR1_1 dell'array: $VAR1_1 da ARRAY21"
}

visualizza
echo

exit 0

#   Come fa notare l'autore,
#+ "lo script può facilmente essere espanso per ottenere gli hash
#+  anche nella shell bash."
#   Esercizio per i lettori (difficile): implementate questa funzionalità.

--

Gli array permettono l'implementazione, in versione di script di shell, del Crivello di Eratostene. Naturalmente, un'applicazione come questa, che fa un uso così intensivo di risorse, in realtà dovrebbe essere scritta in un linguaggio compilato, come il C. Sotto forma di script, la sua esecuzione è atrocemente lenta.

Esempio 26-13. Applicazione complessa di array: Crivello di Eratostene

#!/bin/bash
# sieve.sh (ex68.sh)

# Crivello di Eratostene
# Antico algoritmo per la ricerca di numeri primi.

#  L'esecuzione è di due ordini di grandezza
#+ più lenta dell'equivalente programma scritto in C.

LIMITE_INFERIORE=1       # Si inizia da 1.
LIMITE_SUPERIORE=1000    # Fino a 1000.
# (Potete impostarlo ad un valore più alto. . . se avete tempo a disposizione.)

PRIMO=1
NON_PRIMO=0

let META=LIMITE_SUPERIORE/2
# Ottimizzazione:
# È necessario verificare solamente la metà dei numeri (perché?).


declare -a Primi
# Primi[] è un array.


inizializza ()
{
# Inizializza l'array.

i=$LIMITE_INFERIORE
until [ "$i" -gt "$LIMITE_SUPERIORE" ]
do
  Primi[i]=$PRIMO
  let "i += 1"
done
#  Assumiamo che tutti gli elementi dell'array siano colpevoli (primi)
#+ finché non verrà provata la loro innocenza (non primi).
}

visualizza_primi ()
{
# Visualizza gli elementi dell'array Primi[] contrassegnati come primi.

i=$LIMITE_INFERIORE

until [ "$i" -gt "$LIMITE_SUPERIORE" ]
do

  if [ "${Primi[i]}" -eq "$PRIMO" ]
  then
    printf "%8d" $i
    # 8 spazi per numero producono delle colonne belle ed uniformi.
  fi
  
  let "i += 1"
  
done

}

vaglia () # Identifica i numeri non primi.
{

let i=$LIMITE_INFERIORE+1
# Sappiamo che 1 è primo, quindi iniziamo da 2.

until [ "$i" -gt "$LIMITE_SUPERIORE" ]
do

if [ "${Primi[i]}" -eq "$PRIMO" ]
#  Non si preoccupa di vagliare i numeri già verificati (contrassegnati come
#+ non-primi).
then

  t=$i

  while [ "$t" -le "$LIMITE_SUPERIORE" ]
  do
    let "t += $i "
    Primi[t]=$NON_PRIMO
    # Segna come non-primi tutti i multipli.
  done

fi  

  let "i += 1"
done  


}

# ================================================================
# main ()
# Invoca le funzioni sequenzialmente.
inizializza
vaglia
visualizza_primi
# Questo è ciò che si chiama programmazione strutturata.
# ================================================================

echo

exit 0



# ---------------------------------------------------------------- #
# Il codice oltre la riga precedente non viene eseguito, a cause dell'exit.

#  Questa versione migliorata del Crivello, di Stephane Chazelas,
#+ esegue il compito un po' più velocemente.

#  Si deve invocare con un argomento da riga di comando (il limite dei 
#+ numeri primi).

LIMITE_SUPERIORE=$1            # Da riga di comando.
let META=LIMITE_SUPERIORE/2    # Metà del numero massimo.

Primi=( '' $(seq $LIMITE_SUPERIORE) )

i=1
until (( ( i += 1 ) > META ))  #  È sufficiente verificare solo la metà dei
                               #+ numeri.
do
  if [[ -n $Primi[i] ]]
  then
    t=$i
    until (( ( t += i ) > LIMITE_SUPERIORE ))
    do
      Primi[t]=
    done
  fi
done
echo ${Primi[*]}

exit 0

Si confronti questo generatore di numeri primi, basato sugli array, con uno alternativo che non li utilizza, Esempio A-16.

--

Gli array si prestano, entro certi limiti, a simulare le strutture di dati per le quali Bash non ha un supporto nativo.

Esempio 26-14. Simulare uno stack push-down

#!/bin/bash
# stack.sh: simulazione di uno stack push-down

#  Simile ad uno stack di CPU, lo stack  push-down registra i dati
#+ sequenzialmente, ma li rilascia in ordine inverso, last-in first-out 
#+ (l'ultimo inserito è il primo prelevato).

BP=100            #  Base Pointer (puntatore alla base) dello stack (array).
                  #  Inizio dall'elemento 100.

SP=$BP            #  Stack Pointer (puntatore allo stack).
                  #  Viene inizializzato alla "base" (fondo) dello stack.

Dato=             #  Contenuto di una locazione dello stack.
                  #  Deve essere una variabile locale,
                  #+ a causa della limitazione del valore di ritorno di 
                  #+ una funzione.

declare -a stack


push()            # Pone un dato nello stack.
{
if [ -z "$1" ]    # Niente da immettere?
then
  return
fi

let "SP -= 1"     # Riposiziona lo stack pointer.
stack[$SP]=$1

return
}

pop()                    # Preleva un dato dallo stack.
{
Dato=                    # Svuota la variabile.

if [ "$SP" -eq "$BP" ]   # Lo stack è vuoto?
then
  return
fi                       #  Questo evita anche che SP oltrepassi il 100,
                         #+ cioè, impedisce la "fuga" dallo stack.

Dato=${stack[$SP]}
let "SP += 1"            # Riposiziona lo stack pointer.
return
}

situazione()             # Permette di verificare quello che sta avvenendo.
{
echo "-------------------------------------"
echo "RAPPORTO"
echo "Stack Pointer = $SP"
echo "Appena dopo che \""$Dato"\" è stato prelevato dallo stack."
echo "-------------------------------------"
echo
}


# =======================================================
# Ora un po' di divertimento.

echo

# Vedete se riuscite a prelevare qualcosa da uno stack vuoto.
pop
situazione

echo

push rifiuto
pop
situazione    # Rifiuto inserito, rifiuto tolto.

valore1=23; push $valore1
valore2=skidoo; push $valore2
valore3=FINALE; push $valore3

pop           # FINALE
situazione
pop           # skidoo
situazione
pop           # 23
situazione    # Last-in, first-out!

#  Fate attenzione che lo stack pointer si decrementa ad ogni push,
#+ e si incrementa ad ogni pop.

echo

exit 0

# =======================================================


# Esercizi:
# --------

# 1)  Modificate la funzione "push()" in modo che consenta l'immissione
#   + nello stack di più dati con un'unica chiamata.

# 2)  Modificate la funzione "pop()" in modo che consenta di prelevare
#   + dallo stack più dati con un'unica chiamata.

# 3)  Aggiungete una verifica d'errore nelle funzioni principali che
#   + restituisca un codice d'errore corrispondente al riuscito o
#   + fallito completamento dell'operazione, facendo eseguire 
#   + un'azione appropriata.

# 4)  Utilizzando questo script come base di partenza, scrivete un programma
#   + per una calcolatrice a 4 funzioni basate sullo stack.

#  N.d.T. - Si è preferito lasciare inalterati i termini, in quanto 
#+ appartenenti al linguaggio di programmazione Assembly. La traduzione è 
#+ stata posta tra parentesi o nei commenti.

--

Elaborate manipolazioni dei "subscript" [1] degli array possono richiedere l'impiego di variabili intermedie. In progetti dove questo è richiesto, si consideri, una volta ancora, l'uso di un linguaggio di programmazione più potente, come Perl o C.

Esempio 26-15. Applicazione complessa di array: Esplorare strane serie matematiche

#!/bin/bash

# I celebri "numeri Q" di Douglas Hofstadter:

# Q(1) = Q(2) = 1
# Q(n) = Q(n - Q(n-1)) + Q(n - Q(n-2)), per n>2

#  È una successione di interi "caotica" con comportamento strano e 
#+ non prevedibile.
# I primi 20 numeri della serie sono:
# 1 1 2 3 3 4 5 5 6 6 6 8 8 8 10 9 10 11 11 12

#  Vedi il libro di Hofstadter, "Goedel, Escher, Bach: un'Eterna Ghirlanda 
#+ Brillante", p. 149, ff. (Ed. italiana Adelphi - terza edizione - settembre 
#+ 1985 [N.d.T.])


LIMITE=100      # Numero di termini da calcolare.
AMPIEZZARIGA=20 # Numero di termini visualizzati per ogni riga.

Q[1]=1          # I primi due numeri della serie corrispondono a 1.
Q[2]=1

echo
echo "Numeri Q [$LIMITE termini]:"
echo -n "${Q[1]} "              # Visualizza i primi due termini.
echo -n "${Q[2]} "

for ((n=3; n <= $LIMITE; n++))  # ciclo con condizione in stile C.
do   # Q[n] = Q[n - Q[n-1]] + Q[n - Q[n-2]]  per n>2
#  È necessario suddividere l'espressione in termini intermedi,
#+ perché Bash non è in grado di gestire molto bene la matematica complessa 
#+ degli array.

  let "n1 = $n - 1"        # n-1
  let "n2 = $n - 2"        # n-2
  
  t0=`expr $n - ${Q[n1]}`  # n - Q[n-1]
  t1=`expr $n - ${Q[n2]}`  # n - Q[n-2]
  
  T0=${Q[t0]}              # Q[n - Q[n-1]]
  T1=${Q[t1]}              # Q[n - Q[n-2]]

Q[n]=`expr $T0 + $T1`      # Q[n - Q[n-1]] + Q[n - Q[n-2]]
echo -n "${Q[n]} "

if [ `expr $n % $AMPIEZZARIGA` -eq 0 ]    # Ordina l'output.
then   #      ^ operatore modulo
  echo # Suddivide le righe in blocchi ordinati.
fi

done

echo

exit 0

#  Questa è un'implementazione iterativa dei numeri Q.
#  L'implementazione più intuitiva, che utilizza la ricorsività, è lasciata
#+ come esercizio.
#  Attenzione: calcolare la serie ricorsivamente richiede un tempo MOLTO lungo.

--

Bash supporta solo gli array monodimensionali, sebbene un piccolo stratagemma consenta di simulare quelli multidimensionali.

Esempio 26-16. Simulazione di un array bidimensionale, con suo successivo rovesciamento

#!/bin/bash
# twodim.sh: Simulazione di un array bidimensionale.

# Un array monodimensionale è formato da un'unica riga.
# Un array bidimensionale registra le righe sequenzialmente.

Righe=5
Colonne=5
# Array 5 X 5.

declare -a alfa      # alfa [Righe] [Colonne];
                     # Dichiarazione non necessaria. Perché?

inizializza_alfa ()
{
local rc=0
local indice

for i in A B C D E F G H I J K L M N O P Q R S T U V W X Y
do     # Se preferite, potete utilizzare simboli differenti.
  local riga=`expr $rc / $Colonne`
  local colonna=`expr $rc % $Righe`
  let "indice = $riga * $Righe + $colonna"
  alfa[$indice]=$i   
# alfa[$riga][$colonna]
  let "rc += 1"
done  

#  Sarebbe stato più semplice
#   declare -a alpha=( A B C D E F G H I J K L M N O P Q R S T U V W X Y )
#+ ma, per così dire, si sarebbe perso il "gusto" dell'array bidimensionale.
}

visualizza_alfa ()
{
local riga=0
local indice

echo

while [ "$riga" -lt "$Righe" ] #  Visualizza in ordine di precedenza di riga:
do                             #+ variano le colonne
                               #+ mentre la riga (ciclo esterno) non cambia.
  local colonna=0

  echo -n "       "            #  Allinea l'array "quadrato" con quello ruotato.

  while [ "$colonna" -lt "$Colonne" ]
  do
   let "indice = $riga * $Righe + $colonna"
   echo -n "${alfa[indice]} "  # alfa[$riga][$colonna]
   let "colonna += 1"
  done

  let "riga += 1"
  echo

done

# L'analogo più semplice è
#    echo ${alfa[*]} | xargs -n $Colonne

echo
}

filtra ()     # Elimina gli indici negativi dell'array.
{

echo -n "  "  # Provvede all'inclinazione.
              # Spiegate come.

if [[ "$1" -ge 0 &&  "$1" -lt "$Righe" && "$2" -ge 0 && "$2" -lt "$Colonne" ]]
then
 let "indice = $1 * $Righe + $2"
 # Ora lo visualizza ruotato.
 echo -n " ${alfa[indice]}"  
 #           alfa[$riga][$colonna]
fi    

}
  



ruota ()  #  Ruota l'array di 45 gradi --
{         #+ facendo "perno" sul suo angolo inferiore sinistro.
local riga
local colonna

for (( riga = Righe; riga > -Righe; riga-- ))
do          # Passa l'array in senso inverso. Perché?

  for (( colonna = 0; colonna < Colonne; colonna++ ))
  do

    if [ "$riga" -ge 0 ]
    then
        let "t1 = $colonna - $riga"
        let "t2 = $colonna"
    else
       let "t1 = $colonna"
       let "t2 = $colonna + $riga"
    fi

    filtra $t1 $t2   # Elimina gli indici negativi dell'array.
                     # Cosa succede se non viene fatto?
  done

  echo; echo

done 

#  La rotazione è ispirata agli esempi (pp. 143-146) presenti in
#+ "Advanced C Programming on the IBM PC," di Herbert Mayer
#+ (vedi bibliografia).
#  Questo solo per dimostrare che molto di quello che si può fare con il C
#+ può essere fatto con lo scripting di shell.

}


#--------------- E ora, che lo spettacolo inizi.-------------#
inizializza_alfa  # Inizializza l'array.
visualizza_alfa   # Lo visualizza.
ruota             # Lo ruota di 45 gradi in senso antiorario.
#------------------------------------------------------------#

exit 0

# Si tratta di una simulazione piuttosto macchinosa, per non dire inelegante.
#
# Esercizi:
# ---------
# 1)  Riscrivete le funzioni di inizializzazione e visualizzazione
#     in maniera più intuitiva ed elegante.
#
# 2)  Illustrate come operano le funzioni di rotazione dell'array.
#     Suggerimento: pensate alle implicazioni di una indicizzazione 
#     inversa dell'array.
#
# 3)  Riscrivete lo script in modo da gestire un array non quadrato,
#     come uno di dimensioni 6 X 4.
#     Cercate di minimizzare la "distorsione" quando l'array viene ruotato.

Un array bidimensionale equivale essenzialmente ad uno monodimensionale, ma con modalità aggiuntive per poter individuare, ed eventualmente manipolare, il singolo elemento in base alla sua posizione per riga e colonna.

Per una dimostrazione ancor più elaborata di simulazione di un array bidimensionale, vedi Esempio A-10.

--

Per script più interessanti che impiegano gli array, vedi:

Note

[1]

Con questo termine, nel linguaggio C, vengono chiamati gli indici degli array (N.d.T.)