2012-06-27 16 views
32

Planuję skrypt do zarządzania niektórymi częściami moich systemów Linux i decyduję, czy chcę użyć lub .Wielowymiarowe tablice w Bash

Wolałbym zrobić to jako skrypt Bash, ponieważ polecenia są łatwiejsze, ale prawdziwym czynnikiem decydującym jest konfiguracja. Muszę umieć przechowywać wielowymiarową tablicę w pliku konfiguracyjnym, aby poinformować skrypt, co zrobić ze sobą. Przechowywanie prosty klucz = wartość par w plikach konfiguracyjnych jest dość prosta z bash, ale tylko w ten sposób mogę myśleć zrobić wielowymiarową tablicę jest silnik parsowania dwuwarstwowy, coś

array=&d1|v1;v2;v3&d2|v1;v2;v3 

ale Marshall/kod unmarshall może stać się niedźwiedziem, a jego daleki od przyjaznego dla użytkownika dla następnego słabego soku, który musi go administrować. Jeśli nie mogę tego łatwo zrobić w bashu, po prostu napiszę konfiguracje do pliku xml i napiszę skrypt w pythonie.

Czy jest to prosty sposób w bash?

dzięki wszystkim.

+0

Powiązane: [Jak zadeklarować tablicę 2D w bashie] (http://stackoverflow.com/q/16487258/55075) – kenorb

Odpowiedz

29

Bash nie obsługuje wielowymiarowych tablic ani skrótów, a wydaje się, że chcesz haszować te wartości, które są tablicami. To rozwiązanie nie jest bardzo piękny, to rozwiązanie z pliku xml powinien być lepiej:

array=('d1=(v1 v2 v3)' 'd2=(v1 v2 v3)') 
for elt in "${array[@]}";do eval $elt;done 
echo "d1 ${#d1[@]} ${d1[@]}" 
echo "d2 ${#d2[@]} ${d2[@]}" 
+0

To jest odpowiedź, której się spodziewałem, ale nigdy nie pyta. Dzięki – scphantm

+16

Wystarczy notatkę. bash obsługuje hasze pomocnicze (tablice asocjacyjne) począwszy od wersji 4. więcej informacji: http://mywiki.wooledge.org/BashFAQ/006 – poncha

+0

Tablice asocjacyjne FYI mogą zawierać niektóre z atrybutów 'declare', takie jak 'set' do wielkich przy przypisaniu "jednak nie mogą mieć ustawionej dla nich tablicy -A [asocjacyjnej] lub -a [numerycznej] ani zestawu -n odniesienia, ale możesz oznaczać swoją nazwę zmiennej przez liczbę i użyć zmiennej w miejsce: MYARR_ $ i _ [$ j] byłoby najbliższe temu, ale nie jest to prawdziwa tablica md, ale jest to najlepsze, co dostaniesz. Możesz też użyć funkcji jako systemu pseudo-tablicowego, jeśli jesteś wystarczająco zdesperowany :) – osirisgothra

8

Niezależny powłoki wykorzystywane (sh, ksh, bash, ...) następujące podejście działa całkiem dobrze dla n- tablice wymiarowe (próbka obejmuje dwuwymiarową tablicę).

W próbce separatorem linii (pierwszy wymiar) jest znak spacji. Do wprowadzenia separatora pól (drugi wymiar) używane jest standardowe narzędzie unixowe tr. Dodatkowe separatory dla dodatkowych wymiarów mogą być używane w ten sam sposób.

Oczywiście realizacja tego podejścia nie jest bardzo dobrze, ale jeśli wydajność nie jest kryteria takie podejście jest dość ogólny i może rozwiązać wiele problemów:

array2d="1.1:1.2:1.3 2.1:2.2 3.1:3.2:3.3:3.4" 

function process2ndDimension { 
    for dimension2 in $* 
    do 
     echo -n $dimension2 " " 
    done 
    echo 
} 

function process1stDimension { 
    for dimension1 in $array2d 
    do 
     process2ndDimension `echo $dimension1 | tr : " "` 
    done 
} 

process1stDimension 

Wyjście z tej próbce wygląda następująco:

1.1  1.2  1.3  
2.1  2.2  
3.1  3.2  3.3  3.4 
+0

Nie działa bardzo dobrze, jeśli wartości zawierają spacje lub dwukropki. :) – dannysauer

+1

@dannysauer - powinno być dobrze, jeśli kodujesz spacje i dwukropki jako kod encji lub szesnastkowy. Na przykład% 20 i% 3a. Powinno być dość łatwo przetwarzać dane wejściowe i wyjściowe z macierzy za pomocą takich funkcji, jeśli jest to dla Ciebie niepokojące. – ghoti

+0

Tak więc wartości nie zawierają spacji ani dwukropków. : D – dannysauer

-2
echo "Enter no of terms" 
read count 
for i in $(seq 1 $count) 
do 
    t=` expr $i - 1 ` 
    for j in $(seq $t -1 0) 
    do 
    echo -n " " 
    done 
    j=` expr $count + 1 ` 
    x=` expr $j - $i ` 
    for k in $(seq 1 $x) 
    do 
    echo -n "* " 
    done 
    echo "" 
done 
+1

Zastanów się, aby dodać kilka wyjaśnień na temat tego, co powinien zrobić kod? Chociaż niektóre formatowanie również jest pomocne, niektóre komentarze jeszcze bardziej poprawią odpowiedź. Chociaż widzę wiele tablic, nie widzę tutaj wielowymiarowej tablicy. – Izzy

7

to co pracował dla mnie.

# Define each array and then add it to the main one 
SUB_0=("name0" "value0") 
SUB_1=("name1" "value1") 
MAIN_ARRAY=(
    SUB_0[@] 
    SUB_1[@] 
) 

# Loop and print it. Using offset and length to extract values 
COUNT=${#MAIN_ARRAY[@]} 
for ((i=0; i<$COUNT; i++)) 
do 
    NAME=${!MAIN_ARRAY[i]:0:1} 
    VALUE=${!MAIN_ARRAY[i]:1:1} 
    echo "NAME ${NAME}" 
    echo "VALUE ${VALUE}" 
done 

Jest on oparty off this answer here

+0

powinno to być 'NAZWA = $ {! MAIN_ARRAY [i]: 0: 1}' i 'VALUE = $ {! MAIN_ARRAY [i]: 1: 1}' jeśli się nie mylę, żeby działało (np. wymień 'URLS' dla' GŁÓWNY'). – Christian

2

Rozwijając na odpowiedź Pawła - oto moja wersja pracy z asocjacyjnych podrzędnych tablic w bash:

declare -A SUB_1=(["name1key"]="name1val" ["name2key"]="name2val") 
declare -A SUB_2=(["name3key"]="name3val" ["name4key"]="name4val") 
STRING_1="string1val" 
STRING_2="string2val" 
MAIN_ARRAY=(
    "${SUB_1[*]}" 
    "${SUB_2[*]}" 
    "${STRING_1}" 
    "${STRING_2}" 
) 
echo "COUNT: " ${#MAIN_ARRAY[@]} 
for key in ${!MAIN_ARRAY[@]}; do 
    IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]} 
    echo "VALUE: " ${val[@]} 
    if [[ ${#val[@]} -gt 1 ]]; then 
     for subkey in ${!val[@]}; do 
      subval=${val[$subkey]} 
      echo "SUBVALUE: " ${subval} 
     done 
    fi 
done 

Współpracuje z wartościami mieszanych w głównej tablicy - ciągi/tablice/assoc.Macierze

Kluczem tutaj jest, aby owinąć subarrays w apostrofach i używać * zamiast @ podczas przechowywania subarray wewnątrz głównej tablicy więc byłoby uzyskać przechowywane jako pojedynczy przestrzeni oddzielonej wyrażenie: "${SUB_1[*]}"

Potem ułatwia analizowania tablicę z że gdy przelotowego wartości z IFS=' ' read -a val <<< ${MAIN_ARRAY[$key]}

powyższy kod wyjść:

COUNT: 4 
VALUE: name1val name2val 
SUBVALUE: name1val 
SUBVALUE: name2val 
VALUE: name4val name3val 
SUBVALUE: name4val 
SUBVALUE: name3val 
VALUE: string1val 
VALUE: string2val 
+0

Nie można więc przechowywać ciągów zawierających spacje (nie wspominając o znakach nowej linii).Również brak notowań uniemożliwia bezpieczne używanie znaków globalnych. –

+0

Tak, właśnie uświadomiłem sobie, że podzieli ciągi znaków spacjami na oddzielne podwinięcia, ale ja osobiście mogę z tym żyć, jeśli to tylko wartości konfiguracyjne ... Ale myślę, że to jest najlepsze, co możemy zrobić w bashu . Jestem jednak otwarty na sugestie/ulepszenia. Dodano cytaty, dziękuję za wskazanie :) –

+0

Witam, czy możesz wyjaśnić, w jaki sposób 'IFS = '' odczytaj -a val <<< $ {MAIN_ARRAY [$ key]}' działa. Czym jest IFS? Czy jest tam definiowana zmienna? Dlaczego czytanie jest dołączane do definicji zmiennej? – Boyang

5

atakujących d nie ma wielowymiarowej tablicy. 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).

+1

declare -A [arrayname] wywołuje "nieprawidłową opcję", przynajmniej w OS X. – ktappe

+5

EDIT: Wygląda na to, że OS X zawiera bash 3, a ta opcja wymaga bash 4. Dzięki, Apple. – ktappe

2

Po wielu próbach i błędach znajduję najlepszą, najłatwiejszą i najłatwiejszą wielowymiarową tablicę na bashie, używając zwykłego var. Tak.

Zalety: Nie musisz przechodzić przez dużą tablicę, możesz po prostu powtórzyć "$ var" i użyć grep/awk/sed. Jest to łatwe i jasne i możesz mieć tyle kolumn, ile chcesz.

Przykład:

$ var=$(echo -e 'kris hansen oslo\nthomas jonson peru\nbibi abu johnsonville\njohnny lipp peru') 

$ echo "$var" 
kris hansen oslo 
thomas johnson peru 
bibi abu johnsonville 
johnny lipp peru 

Jeśli chcesz znaleźć każdy w Peru

$ echo "$var" | grep peru 
thomas johnson peru 
johnny lipp peru 

Tylko grep (SED) w polu trzecim

$ echo "$var" | sed -n -E '/(.+) (.+) peru/p' 
thomas johnson peru 
johnny lipp peru 

Jeśli chcesz tylko x pole

$ echo "$var" | awk '{print $2}' 
hansen 
johnson 
abu 
johnny 

Wszyscy w Peru, które nazywa się Thomas i po prostu wrócić jego lastName

$ echo "$var" |grep peru|grep thomas|awk '{print $2}' 
johnson 

Wszelkie zapytania można myśleć ... SuperEasy.

Aby zmienić element:

$ var=$(echo "$var"|sed "s/thomas/pete/") 

Aby usunąć wiersz zawierający „x”

$ var=$(echo "$var"|sed "/thomas/d") 

Aby zmienić kolejne pole w tym samym rzędzie na podstawie wartości od innego elementu

$ var=$(echo "$var"|sed -E "s/(thomas) (.+) (.+)/\1 test \3/") 
$ echo "$var" 
kris hansen oslo                                    
thomas test peru                                   
bibi abu johnsonville 
johnny lipp peru 

Oczywiście działa również pętla, jeśli chcesz to zrobić:

$ for i in "$var"; do echo "$i"; done 
kris hansen oslo 
thomas jonson peru 
bibi abu johnsonville 
johnny lipp peru 

Jedynym haczyka Iv'e gatunki z tego jest to, że koniecznością zawsze zacytować var (w przykładzie; zarówno var i I) lub rzeczy będą wyglądać tak

$ for i in "$var"; do echo $i; done 
kris hansen oslo thomas jonson peru bibi abu johnsonville johnny lipp peru 

i ktoś z pewnością powiedzieć, że to nie będzie działać, jeśli masz przestrzenie w wejściu, jednak, że mogą być mocowane za pomocą innego separatorem w wejściu, np (przy użyciu char utf8 teraz podkreślić, że można wybrać coś wasz wkład nie będą zawierać, ale można wybrać niezależnie OFC):

$ var=$(echo -e 'field one☥field two hello☥field three yes moin\nfield 1☥field 2☥field 3 dsdds aq') 

$ for i in "$var"; do echo "$i"; done 
field one☥field two hello☥field three yes moin 
field 1☥field 2☥field 3 dsdds aq 

$ echo "$var" | awk -F '☥' '{print $3}' 
field three yes moin 
field 3 dsdds aq 

$ var=$(echo "$var"|sed -E "s/(field one)☥(.+)☥(.+)/\1☥test☥\3/") 
$ echo "$var" 
field one☥test☥field three yes moin 
field 1☥field 2☥field 3 dsdds aq 

Jeśli chcesz zapisać nowe linie w swoim wejściu, można konwertować przełamane do czegoś innego przed wprowadzeniem i przekonwertuj go z powrotem na wyjście (lub nie używaj bash ...). Cieszyć się!

+0

Może to działać dla dwóch wymiarów, ale dla ogólnego przypadku o wymiarach jest to trudne. –

+0

To prawda. Kiedy to napisałem, nie zdawałem sobie sprawy z różnicy między 2d, 3d, 4d a wszystkim, czego potrzebowałem, było dobre wsparcie tabelaryczne/db. Teraz nadal nie potrzebowałem niczego więcej niż 2d, ale jeśli ktoś potrzebuje więcej, to przypuszczam, że to nie zadziała. – n00p

0

zrobić to za pomocą tablic asocjacyjnych od atakujących 4 i ustawienie IFS do wartości, która może być zdefiniowany ręcznie.

Celem tego podejścia jest posiadanie tablic jako wartości asocjacyjnych kluczy tablicowych.

Aby przywrócić domyślną wartość IFS, wystarczy ją usunąć.

  • unset IFS

To jest przykład:

#!/bin/bash 

set -euo pipefail 

# used as value in asscciative array 
test=(
    "x3:x4:x5" 
) 
# associative array 
declare -A wow=(
    ["1"]=$test 
    ["2"]=$test 
) 
echo "default IFS" 
for w in ${wow[@]}; do 
    echo " $w" 
done 

IFS=: 
echo "IFS=:" 
for w in ${wow[@]}; do 
    for t in $w; do 
    echo " $t" 
    done 
done 
echo -e "\n or\n" 
for w in ${!wow[@]} 
do 
    echo " $w" 
    for t in ${wow[$w]} 
    do 
    echo " $t" 
    done 
done 

unset IFS 
unset w 
unset t 
unset wow 
unset test 

Wyjście poniżej skryptu jest:

default IFS 
    x3:x4:x5 
    x3:x4:x5 
IFS=: 
    x3 
    x4 
    x5 
    x3 
    x4 
    x5 

or 

    1 
    x3 
    x4 
    x5 
    2 
    x3 
    x4 
    x5 
+0

Nie widzę powodu, aby używać 'test = (" x3: x4: x5 ")', gdy odwołasz się do niego jako '$ test': Możesz po prostu zrobić' test = "x3: x4: x5" '. –

+0

Tak, masz absolutną rację. Dzięki za podpowiedź. – rocksteady

1

jestem delegowania następujące dlatego, że ja s bardzo prosty i jasny sposób naśladowania (przynajmniej w pewnym stopniu) zachowania dwuwymiarowej tablicy w Bashu.Wykorzystuje się tutaj plik (patrz instrukcja Bash) i read (a atakujących wbudowanego polecenia):

## Store the "two-dimensional data" in a file ($$ is just the process ID of the shell, to make sure the filename is unique) 
cat > physicists.$$ <<EOF 
Wolfgang Pauli 1900 
Werner Heisenberg 1901 
Albert Einstein 1879 
Niels Bohr 1885 
EOF 
nbPhysicists=$(wc -l physicists.$$ | cut -sf 1 -d ' ')  # Number of lines of the here-file specifying the physicists. 

## Extract the needed data 
declare -a people  # Create an indexed array (necessary for the read command).                     
while read -ra people; do 
    firstName=${people[0]} 
    familyName=${people[1]} 
    birthYear=${people[2]} 
    echo "Physicist ${firstName} ${familyName} was born in ${birthYear}" 
    # Do whatever you need with data 
done < physicists.$$ 

## Remove the temporary file 
rm physicists.$$ 

wyjściowa: Physicist Wolfgang Pauli was born in 1900 Physicist Werner Heisenberg was born in 1901 Physicist Albert Einstein was born in 1879 Physicist Niels Bohr was born in 1885

Jak to działa:

  • linie w utworzonym pliku tymczasowym odgrywają rolę wektorów jednowymiarowych, w których puste spacje (lub dowolny wybrany znak separacji, patrz opis polecenia read w podręczniku Bash) sepa oceń elementy tych wektorów.
  • Następnie, używając polecenia read z opcją -a, przechodzimy przez pętlę nad każdą linią pliku (aż dojdziemy do końca pliku). Dla każdej linii możemy przypisać pożądane pola (= słowa) do tablicy, którą zadeklarowaliśmy tuż przed pętlą. Opcja -r do polecenia read zapobiega tworzeniu się ukośników odwrotnych jako znaków sterujących, na wypadek gdybyśmy wstawili ukośniki odwrotne w dokumencie tutaj physicists.$$.

Podsumowując plik jest utworzony jako 2D tablicy i jego elementy są pobierane za pomocą pętli na każdej linii i stosując zdolność podporządkowania read przypisanie słowa elementów z (indeksowane) Macierz .

+0

To bardzo podobnie do rozwiązania user7909577. Jednak to, co robisz, to zapisanie sekwencji wierszy w pliku tekstowym. Zatem wymiar 1 jest numerem linii. Wymiar 2 to słowo w linii. Prawdziwie ogólne rozwiązanie umożliwiłoby umieszczenie dowolnej tablicy w dowolnej tablicy. I to nie zadziała. OK, możesz wprowadzić wymiary wciskając linie, ale to wciąż nie jest zbyt eleganckie. –

+0

@U. Windi "umieścił dowolną tablicę w dowolnej tablicy, a to nie zadziała.": Dlatego właśnie w mojej odpowiedzi podkreślam, że ma ona symulować 2D-ARRAYS. "To bardzo podobnie do rozwiązania user7909577": Nie sądzę, że on/ona analizuje ciąg z sed/grep/awk w swojej odpowiedzi i robię to przez "przekształcenie" ciągu znaków w tablicę, a następnie za pomocą indeksów (dlatego symuluje zachowanie tablicy). – Giuseppe

+0

@Guiseppe: Ale oba rozwiązania wykorzystują plik tekstowy jako reprezentację dwuwymiarowej tablicy o wymiarach opisanych przeze mnie. Pytanie dotyczyło w szczególności tablic wielowymiarowych (istnieje oddzielne pytanie dotyczące tablic dwuwymiarowych). –

0

Mam dość prosty, ale inteligentny sposób obejścia: Po prostu zdefiniuj tablicę ze zmiennymi w nazwie. Na przykład:

for ((i=0 ; i<$(($maxvalue + 1)) ; i++)) 
    do 
    for ((j=0 ; j<$(($maxargument + 1)) ; j++)) 
    do 
    declare -a array$i[$j]=((Your rule)) 
    done 
done 

Nie wiem, czy to pomaga, ponieważ nie jest to dokładnie to, o co prosiłeś, ale działa dla mnie. (To samo można osiągnąć za pomocą zmiennych bez tablicy)

Powiązane problemy