2013-05-10 36 views
59

Zastanawiam się w jaki sposób zadeklarować tablicy 2D w bash i następnie zainicjować 0.Jak zadeklarować tablicę 2D w bash

W C wygląda to tak:

int a[4][5] = {0}; 

i jak mogę przypisać wartość do elementu? Jak w C:

a[2][3] = 3; 
+2

related: [wielowymiarowych tablic w bash] (http://stackoverflow.com/q/11233825/2533433) – Izzy

+2

Btw wielowymiarowa tablica jest faktycznie (w głębi) jednowymiarowa tablica, która jest traktowana trochę inaczej, szczególnie jeśli chodzi o acce podążając za jej elementami. Na przykład macierz 3x4 ma 12 komórek. "Rzędy", które przemierzasz z zewnętrzną pętlą z krokiem 3 i "kolumnami" przechodzisz wewnętrzną pętlą z krokiem 1. – rbaleksandar

Odpowiedz

49

Można symulować je na przykład za pomocą skrótów, ale trzeba dbać o wiodących zer i wiele innych rzeczy. Następna demonstracja działa, ale daleko jej do optymalnego rozwiązania.

#!/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 

powyższy przykład tworzy macierz 4x5 z liczb losowych i wydrukować go transponowane, na przykładzie spowodować

  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 

Zasada jest: Tworzenie jednego asocjacyjną, gdzie indeks jest ciągiem jak 3,4. Zalety:

  • możliwe do zastosowania w tablicach o dowolnym wymiarze;) na przykład: 30,40,2 dla 3 wymiarów.
  • składnia jest blisko do „C” jak tablice ${matrix[2,3]}
+1

oczywistą wadą tej metody jest to, że długość wymiaru nie może być znana. Mimo to działa świetnie w większości innych scenariuszy! Dzięki!! –

+0

Proszę wyjaśnić, co robią 'f1' i' f2'? – Jodes

+0

@Jode 'f1' i' f2' zawiera 'format' dla' printf' dla ładnego wyrównanego drukowania.Mogłoby to być zakodowane na stałe, na przykład 'printf '% 2s" 'ale użycie zmiennych jest bardziej elastyczne - jak w powyższym' f1'. "Szerokość" numeru * wiersza * jest obliczana jako długość zmiennej '$ num_rows' - ​​np. jeśli liczba wierszy '$ num_rows' wynosi 9, jego długość wynosi' 1', format będzie wynosił '1 + 1', więc'% 2s'. Dla '$ num_rows' 2500, jego długość wynosi' 4', więc format będzie wynosił '% 5s' - i tak dalej ... – jm666

22

Bash nie obsługuje wielowymiarowych tablic.

Można symulować go jednak za pomocą ekspansji pośrednie:

#!/bin/bash 
declare -a a0=(1 2 3 4) 
declare -a a1=(5 6 7 8) 
var="a1[1]" 
echo ${!var} # outputs 6 

Przydziały są również możliwe przy użyciu tej metody:

let $var=55 
echo ${a1[1]} # outputs 55 

Edycja 1: Aby odczytać taką tablicę z pliku, z każdym wierszem w wierszu i wartościami ograniczonymi spacją użyj tego:

idx=0 
while read -a a$idx; do 
    let idx++; 
done </tmp/some_file 

Edycja 2: Aby zadeklarować i zainicjować a0..a3[0..4] do 0, można uruchomić:

for i in {0..3}; do 
    eval "declare -a a$i=($(for j in {0..4}; do echo 0; done))" 
done 
+0

Czy możesz zademonstrować, jak wypełnić powyższą "symulację tablicy 2D" z pliku -stół? na przykład posiadanie pliku z losową liczbą rzędów iw każdym rzędzie zawierającym 5 rozdzielonych spacjami liczb. – kobame

+0

@kobame: Edytowałem odpowiedź, aby zapewnić rozwiązanie tego, o co prosisz. Będzie czytać tablicę 2d ze zmienną liczbą wierszy i zmienną liczbą kolumn, do a0, a1 i tak dalej. –

+0

W jaki sposób użyjesz innego ogranicznika, na przykład przecinka lub karty? – MountainX

3

Można też podejść do tego w sposób znacznie mniej mądrzejszego mody

q=() 
q+=(1-2) 
q+=(a-b) 

for set in ${q[@]}; 
do 
echo ${set%%-*} 
echo ${set##*-} 
done 

oczywiście roztwór 22 linii lub zadnie to chyba lepiej do zrobienia i dlaczego nie posypać eval każdym miejscem.

+0

Gdzie jest rozwiązanie 22-liniowe? Dla twojego rozwiązania, co zamierzasz zrobić, pisząc skrypt wymagający i/o, a użytkownik chce wprowadzić '-' do 'tablicy'. Również jeśli chcesz symulować tablicę, prawdopodobnie ma sens więcej niż "echo $ {set // - /}" zamiast twoich dwóch. – BroSlow

+0

To był mój błąd, że spudłowałem lub. Myślę, że $ {set // - /} jest prawdopodobnie lepszym rozwiązaniem (nie wiem o problemach z przenośnością %% i ## choć wierzę ci). Co jeśli jest to bardzo niebezpieczne pytanie, jeśli pytasz go wiele razy, że będziesz potrzebować A.I. dla parsera opcji: {p – Prospero

18

Bash nie ma tablicy wielowymiarowej. Ale możesz symulować nieco podobny efekt z tablicami asocjacyjnymi. Poniżej znajduje się przykład asocjacyjna udając być stosowany jako tablicę wielowymiarową:

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 

Jeśli nie zadeklarować tablicę jako stowarzyszeniowej (z -A), powyższe nie zadziała.Na przykład, jeśli pominąć linię declare -A arr The echo wypisze 2 3 zamiast 0 1, ponieważ 0,0, 1,0 i takie będą traktowane jako wyrażenie arytmetyczne i oceniane do 0 (wartość z prawej strony operatora przecinek).

3

Sposób symulować tablic bash (może być dostosowany do dowolnej liczby rozmiarów macierzy):

#!/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 ### 
################ 
4

Innym podejściem jest to można reprezentowania każdego wiersza w postaci łańcucha, tj mapującą układ 2D do tablicy 1D. Następnie wszystko co musisz zrobić, to rozpakować i zapakować ciąg rzędu za każdym razem, gdy dokonać EDIT:

# 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 

Wyjścia:

0 0 0 0 0 
0 0 0 0 0 
0 0 0 3 0 
0 0 0 0 0 
1

Można po prostu zdefiniować dwie funkcje do pisania (4 $ jest przypisana wartość) i odczytać macierz z dowolną nazwą (1 USD) i indeksami (2 USD i 3 USD) wykorzystując eval i pośrednie referencje.

#!/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 
+2

Cześć, dodaj trochę objaśnień wraz z kodem, ponieważ pomaga to zrozumieć twój kod. Odpowiedzi tylko na kod są niezadowolone. –

1

Jeśli każdy rząd macierzy ma taki sam rozmiar, można po prostu użyć macierzy liniowej i mnożenia.

Oznacza to,

a=() 
for ((i=0; i<4; ++i)); do 
    for ((j=0; j<5; ++j)); do 
    a[i*5+j]=0 
    done 
done 

Wtedy twój a[2][3] = 3 staje

a[2*5+3] = 3 

Podejście to może być warte przekształcając zestaw funkcji, ale ponieważ nie można przejść do tablic lub tablice z powrotu funkcje, musiałbyś używać pass-by-name, a czasem eval. Tak więc mam tendencję do składania wielowymiarowych tablic pod "rzeczy, których bash jest po prostu nie do zniesienia".

0

do symulowania 2-wymiarową tablicę, najpierw załadować pierwszych n-elementów (z elementów pierwszej kolumnie)

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 

celu dodania drugiej kolumnie, że określenie wielkości w pierwszej kolumnie i obliczyć wartości offsetowej zmiennych

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 
Powiązane problemy