Ich frage mich, wie man ein 2D-Array in Bash deklariert und dann auf 0 initialisiert.
In C sieht es so aus:
int a[4][5] = {0};
Und wie ordne ich einem Element einen Wert zu? Wie in C:
a[2][3] = 3;
Ich frage mich, wie man ein 2D-Array in Bash deklariert und dann auf 0 initialisiert.
In C sieht es so aus:
int a[4][5] = {0};
Und wie ordne ich einem Element einen Wert zu? Wie in C:
a[2][3] = 3;
Antworten:
Sie können sie beispielsweise mit Hashes simulieren, müssen sich jedoch um die führenden Nullen und viele andere Dinge kümmern. Die nächste Demonstration funktioniert, ist aber alles andere als eine optimale Lösung.
#!/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
Im obigen Beispiel wird eine 4x5-Matrix mit Zufallszahlen erstellt und mit dem Beispielergebnis transponiert gedruckt
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
Das Prinzip lautet: Erstellen eines assoziativen Arrays, bei dem der Index einer Zeichenfolge ähnelt 3,4
. Die Vorteile:
30,40,2
für 3-dimensionale.${matrix[2,3]}
f1
und f2
was?
f1
und f2
enthält das format
für das printf
für den schön ausgerichteten Drucken. Es könnte zum Beispiel fest codiert sein, printf "%2s"
aber die Verwendung von Variablen ist flexibler - wie oben f1
. Der width
von der Zeilennummer wird als die Länge der berechneten $num_rows
Variable - zB wenn die Anzahl der Zeilen $num_rows
9 ist, seine Länge ist 1
das Format wird 1+1
so %2s
. Für den $num_rows
2500 ist seine Länge 4
so, dass das Format sein wird %5s
- und so weiter ...
Bash unterstützt keine mehrdimensionalen Arrays.
Sie können es jedoch mithilfe der indirekten Erweiterung simulieren:
#!/bin/bash
declare -a a0=(1 2 3 4)
declare -a a1=(5 6 7 8)
var="a1[1]"
echo ${!var} # outputs 6
Zuweisungen sind auch mit dieser Methode möglich:
let $var=55
echo ${a1[1]} # outputs 55
Bearbeiten 1 : Verwenden Sie Folgendes, um ein solches Array aus einer Datei mit jeder Zeile in einer Zeile und durch Leerzeichen getrennten Werten zu lesen:
idx=0
while read -a a$idx; do
let idx++;
done </tmp/some_file
Bearbeiten 2 : Zum Deklarieren und Initialisieren a0..a3[0..4]
von 0
können Sie Folgendes ausführen:
for i in {0..3}; do
eval "declare -a a$i=( $(for j in {0..4}; do echo 0; done) )"
done
Bash hat kein mehrdimensionales Array. Mit assoziativen Arrays können Sie jedoch einen ähnlichen Effekt simulieren. Das Folgende ist ein Beispiel für ein assoziatives Array, das vorgibt, als mehrdimensionales Array verwendet zu werden:
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
Wenn Sie das Array nicht als assoziativ (mit -A
) deklarieren , funktioniert das oben Gesagte nicht. Wenn Sie beispielsweise die declare -A arr
Zeile weglassen , echo
wird 2 3
anstelle von 0 1
, weil gedruckt 0,0
, 1,0
und dies wird als arithmetischer Ausdruck verwendet und als 0
(der Wert rechts vom Kommaoperator) ausgewertet .
Sie können dies auch viel weniger intelligent angehen
q=()
q+=( 1-2 )
q+=( a-b )
for set in ${q[@]};
do
echo ${set%%-*}
echo ${set##*-}
done
Natürlich ist eine 22-Zeilen-Lösung oder Indirektion wahrscheinlich der bessere Weg, und warum nicht überall eval streuen.
-
in das 'Array' eingeben möchte ? Auch wenn Sie ein Array simulieren möchten, ist dies wahrscheinlich sinnvoller als echo ${set//-/ }
Ihre beiden.
Ein anderer Ansatz besteht darin, dass Sie jede Zeile als Zeichenfolge darstellen können, dh das 2D-Array einem 1D-Array zuordnen. Dann müssen Sie nur noch die Zeichenfolge der Zeile entpacken und neu packen, wenn Sie eine Bearbeitung vornehmen:
# Init a 4x5 matrix
a=("00 01 02 03 04" "10 11 12 13 14" "20 21 22 23 24" "30 31 32 33 34")
aset() {
row=$1
col=$2
value=$3
IFS=' ' read -r -a tmp <<< "${a[$row]}"
tmp[$col]=$value
a[$row]="${tmp[@]}"
}
# Set a[2][3] = 9999
aset 2 3 9999
# Show result
for r in "${a[@]}"; do
echo $r
done
Ausgänge:
00 01 02 03 04
10 11 12 13 14
20 21 22 9999 24
30 31 32 33 34
Eine Möglichkeit, Arrays in Bash zu simulieren (kann für eine beliebige Anzahl von Dimensionen eines Arrays angepasst werden):
#!/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 ###
################
Wenn jede Zeile der Matrix dieselbe Größe hat, können Sie einfach ein lineares Array und eine Multiplikation verwenden.
Das ist,
a=()
for (( i=0; i<4; ++i )); do
for (( j=0; j<5; ++j )); do
a[i*5+j]=0
done
done
Dann a[2][3] = 3
wird
a[2*5+3] = 3
Es könnte sich lohnen, diesen Ansatz in eine Reihe von Funktionen umzuwandeln. Da Sie jedoch keine Arrays an Funktionen übergeben oder von diesen zurückgeben können, müssen Sie manchmal die Namensübergabe verwenden eval
. Daher neige ich dazu, mehrdimensionale Arrays unter "Dinge, die Bash einfach nicht zu tun hat" abzulegen.
Man kann einfach zwei Funktionen definieren, die geschrieben werden sollen ($ 4 ist der zugewiesene Wert) und eine Matrix mit einem beliebigen Namen ($ 1) und Indizes ($ 2 und $ 3) lesen, wobei eval und indirekte Referenzierung ausgenutzt werden.
#!/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
2D-Array kann in Bash erreicht werden, indem 1D-Array deklariert wird, und dann kann mit auf Elemente zugegriffen werden (r * col_size) + c)
. Die folgende Logik löscht 1D-Array ( str_2d_arr
) und druckt als 2D-Array.
col_size=3
str_2d_arr=()
str_2d_arr+=('abc' '200' 'xyz')
str_2d_arr+=('def' '300' 'ccc')
str_2d_arr+=('aaa' '400' 'ddd')
echo "Print 2D array"
col_count=0
for elem in ${str_2d_arr[@]}; do
if [ ${col_count} -eq ${col_size} ]; then
echo ""
col_count=0
fi
echo -e "$elem \c"
((col_count++))
done
echo ""
Ausgabe ist
Print 2D array
abc 200 xyz
def 300 ccc
aaa 400 ddd
Die folgende Logik ist sehr nützlich, um jede Zeile aus dem oben deklarierten 1D-Array abzurufen str_2d_arr
.
# Get nth row and update to 2nd arg
get_row_n()
{
row=$1
local -n a=$2
start_idx=$((row * col_size))
for ((i = 0; i < ${col_size}; i++)); do
idx=$((start_idx + i))
a+=(${str_2d_arr[${idx}]})
done
}
arr=()
get_row_n 0 arr
echo "Row 0"
for e in ${arr[@]}; do
echo -e "$e \c"
done
echo ""
Ausgabe ist
Row 0
abc 200 xyz
Um ein zweidimensionales Array zu simulieren, lade ich zuerst die ersten n-Elemente (die Elemente der ersten Spalte).
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
Um die zweite Spalte hinzuzufügen, definiere ich die Größe der ersten Spalte und berechne die Werte in einer Versatzvariablen
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
Der folgende Code funktioniert auf jeden Fall, vorausgesetzt, Sie arbeiten auf einem Mac mit Bash-Version 4. Sie können nicht nur 0 deklarieren, sondern dies ist eher ein universeller Ansatz zum dynamischen Akzeptieren von Werten.
declare -A arr
echo "Enter the row"
read r
echo "Enter the column"
read c
i=0
j=0
echo "Enter the elements"
while [ $i -lt $r ]
do
j=0
while [ $j -lt $c ]
do
echo $i $j
read m
arr[${i},${j}]=$m
j=`expr $j + 1`
done
i=`expr $i + 1`
done
i=0
j=0
while [ $i -lt $r ]
do
j=0
while [ $j -lt $c ]
do
echo -n ${arr[${i},${j}]} " "
j=`expr $j + 1`
done
echo ""
i=`expr $i + 1`
done
Mark Reed schlug eine sehr gute Lösung für 2D-Arrays (Matrix) vor! Sie können immer in ein 1D-Array (Vektor) konvertiert werden. Obwohl Bash keine native Unterstützung für 2D-Arrays bietet, ist es nicht so schwierig, eine einfache ADT nach dem genannten Prinzip zu erstellen.
Hier ist ein Barebone-Beispiel ohne Argumentprüfungen usw., um die Lösung klar zu halten: Die Größe des Arrays wird als zwei erste Elemente in der Instanz festgelegt (Dokumentation für das Bash-Modul, das eine Matrix-ADT implementiert, https://github.com) /vorakl/bash-libs/blob/master/src.docs/content/pages/matrix.rst )
#!/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