Je me demande comment déclarer un tableau 2D dans bash, puis initialiser à 0.
En C ça ressemble à ça:
int a[4][5] = {0};
Et comment attribuer une valeur à un élément? Comme en C:
a[2][3] = 3;
Vous pouvez les simuler par exemple avec des hachages, mais vous devez vous soucier des zéros non significatifs et de bien d’autres choses. La prochaine démonstration fonctionne, mais la solution est loin d’être optimale.
#!/bin/bash
declare -A matrix
num_rows=4
num_columns=5
for ((i=1;i<=num_rows;i++)) do
for ((j=1;j<=num_columns;j++)) do
matrix[$i,$j]=$RANDOM
done
done
f1="%$((${#num_rows}+1))s"
f2=" %9s"
printf "$f1" ''
for ((i=1;i<=num_rows;i++)) do
printf "$f2" $i
done
echo
for ((j=1;j<=num_columns;j++)) do
printf "$f1" $j
for ((i=1;i<=num_rows;i++)) do
printf "$f2" ${matrix[$i,$j]}
done
echo
done
l'exemple ci-dessus crée une matrice 4x5 avec des nombres aléatoires et l'imprime transposée, avec le résultat de l'exemple
1 2 3 4
1 18006 31193 16110 23297
2 26229 19869 1140 19837
3 8192 2181 25512 2318
4 3269 25516 18701 7977
5 31775 17358 4468 30345
Le principe est le suivant: Création d'un tableau associatif où l'index est une chaîne telle que 3,4
. Les avantages:
30,40,2
pour 3 dimensions.${matrix[2,3]}
Bash ne prend pas en charge les tableaux multidimensionnels.
Vous pouvez cependant le simuler en utilisant une expansion indirecte:
#!/bin/bash
declare -a a0=(1 2 3 4)
declare -a a1=(5 6 7 8)
var="a1[1]"
echo ${!var} # outputs 6
Les affectations sont également possibles avec cette méthode:
let $var=55
echo ${a1[1]} # outputs 55
Edit 1: Pour lire un tel tableau dans un fichier, avec chaque ligne sur une ligne, et les valeurs délimitées par un espace, utilisez ceci:
idx=0
while read -a a$idx; do
let idx++;
done </tmp/some_file
Edit 2: Pour déclarer et initialiser a0..a3[0..4]
sur 0
, vous pouvez exécuter:
for i in {0..3}; do
eval "declare -a a$i=( $(for j in {0..4}; do echo 0; done) )"
done
Bash n'a pas de tableau multi-dimensionnel. Mais vous pouvez simuler un effet quelque peu similaire avec des tableaux associatifs. Voici un exemple de tableau associatif prétendant être utilisé comme tableau multidimensionnel:
declare -A arr
arr[0,0]=0
arr[0,1]=1
arr[1,0]=2
arr[1,1]=3
echo "${arr[0,0]} ${arr[0,1]}" # will print 0 1
Si vous ne déclarez pas le tableau comme étant associatif (avec -A
), cela ne fonctionnera pas. Par exemple, si vous omettez la ligne declare -A arr
, la echo
imprimera 2 3
au lieu de 0 1
, car 0,0
, 1,0
et ainsi de suite. être considéré comme une expression arithmétique et évalué comme 0
(la valeur à droite de l'opérateur de virgule).
Vous pouvez également aborder ceci de manière beaucoup moins intelligente
q=()
q+=( 1-2 )
q+=( a-b )
for set in ${q[@]};
do
echo ${set%%-*}
echo ${set##*-}
done
bien entendu, une solution ou une option de 22 lignes est probablement la meilleure solution et pourquoi ne pas saupoudrer eval partout.
Une façon de simuler des tableaux dans bash (il peut être adapté à n’importe quel nombre de dimensions d’un tableau):
#!/bin/bash
## The following functions implement vectors (arrays) operations in bash:
## Definition of a vector <v>:
## v_0 - variable that stores the number of elements of the vector
## v_1..v_n, where n=v_0 - variables that store the values of the vector elements
VectorAddElementNext () {
# Vector Add Element Next
# Adds the string contained in variable $2 in the next element position (vector length + 1) in vector $1
local elem_value
local vector_length
local elem_name
eval elem_value=\"\$$2\"
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
vector_length=$(( vector_length + 1 ))
elem_name=$1_$vector_length
eval $elem_name=\"\$elem_value\"
eval $1_0=$vector_length
}
VectorAddElementDVNext () {
# Vector Add Element Direct Value Next
# Adds the string $2 in the next element position (vector length + 1) in vector $1
local elem_value
local vector_length
local elem_name
eval elem_value="$2"
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
vector_length=$(( vector_length + 1 ))
elem_name=$1_$vector_length
eval $elem_name=\"\$elem_value\"
eval $1_0=$vector_length
}
VectorAddElement () {
# Vector Add Element
# Adds the string contained in the variable $3 in the position contained in $2 (variable or direct value) in the vector $1
local elem_value
local elem_position
local vector_length
local elem_name
eval elem_value=\"\$$3\"
elem_position=$(($2))
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
if [ $elem_position -ge $vector_length ]; then
vector_length=$elem_position
fi
elem_name=$1_$elem_position
eval $elem_name=\"\$elem_value\"
if [ ! $elem_position -eq 0 ]; then
eval $1_0=$vector_length
fi
}
VectorAddElementDV () {
# Vector Add Element
# Adds the string $3 in the position $2 (variable or direct value) in the vector $1
local elem_value
local elem_position
local vector_length
local elem_name
eval elem_value="$3"
elem_position=$(($2))
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
if [ $elem_position -ge $vector_length ]; then
vector_length=$elem_position
fi
elem_name=$1_$elem_position
eval $elem_name=\"\$elem_value\"
if [ ! $elem_position -eq 0 ]; then
eval $1_0=$vector_length
fi
}
VectorPrint () {
# Vector Print
# Prints all the elements names and values of the vector $1 on sepparate lines
local vector_length
vector_length=$(($1_0))
if [ "$vector_length" = "0" ]; then
echo "Vector \"$1\" is empty!"
else
echo "Vector \"$1\":"
for ((i=1; i<=$vector_length; i++)); do
eval echo \"[$i]: \\\"\$$1\_$i\\\"\"
###OR: eval printf \'\%s\\\n\' \"[\$i]: \\\"\$$1\_$i\\\"\"
done
fi
}
VectorDestroy () {
# Vector Destroy
# Empties all the elements values of the vector $1
local vector_length
vector_length=$(($1_0))
if [ ! "$vector_length" = "0" ]; then
for ((i=1; i<=$vector_length; i++)); do
unset $1_$i
done
unset $1_0
fi
}
##################
### MAIN START ###
##################
## Setting vector 'params' with all the parameters received by the script:
for ((i=1; i<=$#; i++)); do
eval param="\${$i}"
VectorAddElementNext params param
done
# Printing the vector 'params':
VectorPrint params
read temp
## Setting vector 'params2' with the elements of the vector 'params' in reversed order:
if [ -n "$params_0" ]; then
for ((i=1; i<=$params_0; i++)); do
count=$((params_0-i+1))
VectorAddElement params2 count params_$i
done
fi
# Printing the vector 'params2':
VectorPrint params2
read temp
## Getting the values of 'params2'`s elements and printing them:
if [ -n "$params2_0" ]; then
echo "Printing the elements of the vector 'params2':"
for ((i=1; i<=$params2_0; i++)); do
eval current_elem_value=\"\$params2\_$i\"
echo "params2_$i=\"$current_elem_value\""
done
else
echo "Vector 'params2' is empty!"
fi
read temp
## Creating a two dimensional array ('a'):
for ((i=1; i<=10; i++)); do
VectorAddElement a 0 i
for ((j=1; j<=8; j++)); do
value=$(( 8 * ( i - 1 ) + j ))
VectorAddElementDV a_$i $j $value
done
done
## Manually printing the two dimensional array ('a'):
echo "Printing the two-dimensional array 'a':"
if [ -n "$a_0" ]; then
for ((i=1; i<=$a_0; i++)); do
eval current_vector_lenght=\$a\_$i\_0
if [ -n "$current_vector_lenght" ]; then
for ((j=1; j<=$current_vector_lenght; j++)); do
eval value=\"\$a\_$i\_$j\"
printf "$value "
done
fi
printf "\n"
done
fi
################
### MAIN END ###
################
Une autre approche consiste à représenter chaque ligne sous forme de chaîne, c'est-à-dire à mapper le tableau 2D en un tableau 1D. Ensuite, tout ce que vous avez à faire est de décompresser et de remballer la chaîne de la ligne chaque fois que vous effectuez une modification:
# Init a 4x5 matrix
a=("0 0 0 0 0" "0 0 0 0 0" "0 0 0 0 0" "0 0 0 0 0")
function aset {
IFS=' ' read -r -a tmp <<< "${a[$1]}"
tmp[$2]=$3
a[$1]="${tmp[@]}"
}
# Set a[2][3] = 3
aset 2 3 3
# Show result
for r in "${a[@]}"; do
echo $r
done
Les sorties:
0 0 0 0 0
0 0 0 0 0
0 0 0 3 0
0 0 0 0 0
Si chaque ligne de la matrice a la même taille, vous pouvez simplement utiliser un tableau linéaire et une multiplication.
C'est,
a=()
for (( i=0; i<4; ++i )); do
for (( j=0; j<5; ++j )); do
a[i*5+j]=0
done
done
Alors votre a[2][3] = 3
devient
a[2*5+3] = 3
Cette approche peut valoir la peine d’être transformée en un ensemble de fonctions, mais comme vous ne pouvez pas passer de tableaux à des fonctions ou renvoyer de tableaux à partir de fonctions, vous devez utiliser un mot de passe et parfois eval
. J'ai donc tendance à classer des tableaux multidimensionnels sous "tout ce que bash n'est simplement pas censé faire".
On peut simplement définir deux fonctions à écrire ($ 4 est la valeur attribuée) et lire une matrice avec un nom arbitraire ($ 1) et des index ($ 2 et $ 3) exploitant les références eval et indirecte.
#!/bin/bash
matrix_write () {
eval $1"_"$2"_"$3=$4
# aux=$1"_"$2"_"$3 # Alternative way
# let $aux=$4 # ---
}
matrix_read () {
aux=$1"_"$2"_"$3
echo ${!aux}
}
for ((i=1;i<10;i=i+1)); do
for ((j=1;j<10;j=j+1)); do
matrix_write a $i $j $[$i*10+$j]
done
done
for ((i=1;i<10;i=i+1)); do
for ((j=1;j<10;j=j+1)); do
echo "a_"$i"_"$j"="$(matrix_read a $i $j)
done
done
Pour simuler un tableau à 2 dimensions, je commence par charger les n premiers éléments (les éléments de la première colonne)
local pano_array=()
i=0
for line in $(grep "filename" "$file")
do
url=$(extract_url_from_xml $line)
pano_array[i]="$url"
i=$((i+1))
done
Pour ajouter la deuxième colonne, je définis la taille de la première colonne et calcule les valeurs dans une variable offset
array_len="${#pano_array[@]}"
i=0
while [[ $i -lt $array_len ]]
do
url="${pano_array[$i]}"
offset=$(($array_len+i))
found_file=$(get_file $url)
pano_array[$offset]=$found_file
i=$((i+1))
done
Mark Reed a suggéré une très bonne solution pour les tableaux 2D (matrice)! Ils peuvent toujours être convertis dans un tableau 1D (vecteur). Bien que Bash n’ait pas de support natif pour les tableaux 2D, il n’était pas si difficile de créer un simple ADT autour du principe mentionné.
Voici un exemple simple, sans contrôle d'arguments, etc., pour que la solution reste claire: la taille du tableau est définie comme deux premiers éléments de l'instance.
#!/bin/bash
matrix_init() {
# matrix_init instance x y data ...
declare -n self=$1
declare -i width=$2 height=$3
shift 3;
self=(${width} ${height} "$@")
}
matrix_get() {
# matrix_get instance x y
declare -n self=$1
declare -i x=$2 y=$3
declare -i width=${self[0]} height=${self[1]}
echo "${self[2+y*width+x]}"
}
matrix_set() {
# matrix_set instance x y data
declare -n self=$1
declare -i x=$2 y=$3
declare data="$4"
declare -i width=${self[0]} height=${self[1]}
self[2+y*width+x]="${data}"
}
matrix_destroy() {
# matrix_destroy instance
declare -n self=$1
unset self
}
# my_matrix[3][2]=( (one, two, three), ("1 1" "2 2" "3 3") )
matrix_init my_matrix \
3 2 \
one two three \
"1 1" "2 2" "3 3"
# print my_matrix[2][0]
matrix_get my_matrix 2 0
# print my_matrix[1][1]
matrix_get my_matrix 1 1
# my_matrix[1][1]="4 4 4"
matrix_set my_matrix 1 1 "4 4 4"
# print my_matrix[1][1]
matrix_get my_matrix 1 1
# remove my_matrix
matrix_destroy my_matrix