ubuntuusers.de

Verarbeitung von Arrays

Status: Ungelöst | Ubuntu-Version: Ubuntu 16.04 (Xenial Xerus)
Antworten |

Standard1988

Anmeldungsdatum:
28. Dezember 2010

Beiträge: 87

Hallo Zusammen,

ich habe ein File (list.lst) mit mehreren Arrays:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
TEST_A=(
'1a'
'2a'
'3a'
)

TEST_B=(
'1b'
'2b'
'3b'
)

Jetzt möchte ich alle Elementen von allen Arrays über eine Forschleife in einem Script verarbeiten möchte:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/bash

LST=list.lst

for i in `grep "^.*.=" $LST | cut -f1 -d"="`;
do
        for tag in ${i[*]};
        do
        echo $tag
        done
done

Ich bekomme als Ergebnis durch das echo dies:

TEST_A

TEST_B

Es sollte aber folgendes herauskommen:

1a

2a

3a

1b

2b

3b

Habt ihr eine Idee?

Viele Grüße

lnix

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11248

Wohnort: München

Wenn man die Namen der definierten Arrays eindeutig in den Dateien erkennen kann (und deren Namen keine Variablen überschreiben, die sonst im Skript benötigt werden), könnte man die Datei mit den Arrays sourcen und dann über die gefundenen Definitonen für die Arrays iterieren:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
##!/bin/bash
LST="list.lst"

source "$LST"
for i in $(grep -Po "^\w+(?=\=)" "$LST"); do  # Wir suchen nach Zeichenketten am Zeilenanfang ohne Whitespace, die von einem "=" gefolgt werden
    eval current_array=( '"${'$i'[@]}"' )    # hässlicher Trick, um über den String in der Variable $i an den Inhalt des eingelesenen Array zu kommen
    for element in ${current_array[@]}; do
        echo $element
    done
done

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 13174

Man braucht auf jeden Fall eval.

1
2
3
4
5
source "$1"

for var in $(sed -ne 's#^\([^=]\+\)=.*#\1#p' "$1"); do
  eval "printf '%s\\n' \"\${$var[@]}\""
done

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2505

Hübsch wird das alles nicht und ich sehe auch keinen Weg, wie du um ein „eval“ herumkommen könntest.

Hier nochmal eine andere Strategie. Er lässt sich mit „set“ alle Shell-Variablen anzeigen und filtert dann die Arrays heraus. Das macht er vor und nach dem Sourcen der Liste. „comm“ kann dann die neu dazugekommenen Arraynamen herausfischen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

arrays_before=$(set | sed -rn 's/^([_a-zA-Z0-9]+)=\(.*/\1/p')
source liste
arrays_after=$(set | sed -rn 's/^([_a-zA-Z0-9]+)=\(.*/\1/p')

arrays_new=$(comm -3 <(echo "$arrays_before") <(echo "$arrays_after"))

for i in $arrays_new
do
    # Bash-interne Variable, die durch die Pipeline mit set/sed zustande
    # kam.
    [[ "$i" == PIPESTATUS ]] && continue

    # Erzeugt das neue Array "arr", das mit den Elementen (nicht jedoch
    # den Keys, falls welche gesetzt wären!) befüllt wird.
    eval arr=(\"\${$i[@]}\")

    for j in "${arr[@]}"
    do
        echo "<$i>: <$j>"
    done
done

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn ()
for a
do
        typeset -a 'A=("${'"${a}"'[@]}")'
        for aa in "${A[@]}"
        do
                echo "$aa"
        done
done

source LST
fn ${!TEST*}

:

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17604

Wohnort: Berlin

1
2
3
4
5
6
7
sed "/[()].*/d;s/'//g;/^$/d" test.lst 
1a
2a
3a
1b
2b
3b
  • Alles mit runden Klammern löschen (d=delete)

  • Apostrophe ersetzen durch nix (substitute)

  • leere Zeilen (Zeilenanfang ^, gefolgt von Zeilenende $) löschen.

track

Avatar von track

Anmeldungsdatum:
26. Juni 2008

Beiträge: 7174

Wohnort: Wolfen (S-A)

... sowas ist aber von hinten durch die Brust ins Auge !

  1. Du musst jemanden haben, der Dir die Zeilen zwischen den Klammern einsammelt und daraus jeweils ein Array macht. Das kann z.B. sed sein:

    track@track:~$ sed -n '/(/,/)/ H; /)/ {s/.*//; x; s/\n/ /gp}'  test_ab
     TEST_A=( '1a' '2a' '3a' )
     TEST_B=( '1b' '2b' '3b' ) 
  2. Dann kannst Du den ganzen Kram auch sourcen:

    track@track:~$ .  <(sed -n '/(/,/)/ H; /)/ {s/.*//; x; s/\n/ /gp}'  test_ab)
    track@track:~$ echo "${TEST_A[0]} .. ${TEST_A[1]} ... ${TEST_A[2]}  und B: ${TEST_B[0]}"
    1a .. 2a ... 3a  und B: 1b 

    - Du siehst, dass dann auch alles hübsch als Shell-Array vorliegt.

Aber ehrlich gesagt: sowas macht man eigentlich nicht !

LG,

track

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

Neben Meta-Abarbeit des Sourcefiles und direktes Ansprechen der Array-Namen kommen mir noch in den Sinn:

  • manuell ein Meta-Array erstellen mit den Namen der normalen Arrays

  • declare -ap benutzen und parsen

  • zu jedem Array eine namref mit ähnlichem Namen erstellen und dann declare -np benutzen oder testen mit [[ -n ]]

  • alle Arrays in Funktionen einbetten und declare -f benutzen

Ein "kreatives" Beispiel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
typeset -A Arrays

def ()
{
        typeset a="$_"
        Arrays[${a%%=*}]=${a#*=}
        . <(echo "$a")
}

: TEST_A="(
        '1a'
        '2a'
        '3a'
)"
def

: TEST_B="(
        '1b'
        '2b'
        '3b'
)"
def

Und dann:

1
2
3
4
5
6
7
8
for _a in "${!Arrays[@]}"
do
        typeset -a 'a=("${'"${_a}"'[@]}")'
        for __a in "${a[@]}"
        do
                echo "$__a"
        done
done

Edit:

  • Namen angepasst

TausB

Avatar von TausB

Anmeldungsdatum:
26. November 2009

Beiträge: 1570

Wohnort: Terra incognita

Mein Vorschlag ohne eval (Grüße an rklm und Vain ☺):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
LST="list.lst"
source "$LST"

for var in $(sed -ne 's#^\([^=]\+\)=.*#\1#p' "$LST"); do
  array="$var[@]"
  for x in ${!array}; do
	echo "${x}"
	done
done

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

TausB schrieb:

Mein Vorschlag ohne eval (Grüße an rklm und Vain ☺):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/bin/bash
LST="list.lst"
source "$LST"

for var in $(sed -ne 's#^\([^=]\+\)=.*#\1#p' "$LST"); do
  array="$var[@]"
  for x in ${!array}; do
	echo "${x}"
	done
done

Jao. Ich halte trotzdem mal dagegen: 1) Man weiß ja nicht genau, was in der for-Schleife alles passieren soll. Auf Indizes kann jetzt nicht mehr zugegriffen werden; daher müsste dann doch ein array2=("${!array}") folgen?! – 2) Die Syntax kann von einer Version auf die nächste verschwinden.

Siehe zum gesamtem Komplex BashFAQ/006, Suche: "risk hack"

TausB

Avatar von TausB

Anmeldungsdatum:
26. November 2009

Beiträge: 1570

Wohnort: Terra incognita

D630 schrieb:

2) Die Syntax kann von einer Version auf die nächste verschwinden.

Siehe zum gesamtem Komplex BashFAQ/006, Suche: "risk hack"

Nutze ich bereits seit Jahren - natürlich kann ich nicht in die Zukunft gucken, aber Updates sind hier doch recht überschaubar.

PS: Warum hast Du dann selbst:

...
for _a in "${!Arrays[@]}"
...

vorgeschlagen? Abgesehen davan, dass diese Konstrukt (bei mir) überhaupt nicht funktioniert.

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

TausB schrieb:

PS: Warum hast Du dann selbst:

...
for _a in "${!Arrays[@]}"
...

vorgeschlagen? Abgesehen davan, dass diese Konstrukt (bei mir) überhaupt nicht funktioniert.

:

Moment, das Vorgehen besteht ja aus zwei Schritten: a) an die Namen der indizierten Arrays herankommen und b) diese Arrays später in einer for-Loop benutzen. Dein Vorschlag, der ohne eval auskommt, bezieht sich auf b); für a) benutzt Du sed. Das in den BashFAQ als "Hack" Beschriebene ist nicht die Indirection an sich, sondern sie zu benutzen, um an Arrays heranzukommen, obwohl sie eigentlich für Skalare gedacht ist. Gemeint ist also array="$var[@]". Hack hin oder her, unter gleichbleibenden Bedingungen kann man es ja machen; die Frage ist ja eher, ob in Schritt b) auf die Indizes zugegriffen werden können sollte. Wenn ja, müsste Dein Vorschlag ja noch erweitert werden (wenn es weitere Wege gibt, her damit!):

1
2
3
4
5
6
7
for var in wordlist; do
  array="$var[@]"
  array2=("${!array}")
  for x in ${array2[@]}; do
	echo "${x}"
	done
done

Hier wird ein array2 gebraucht; Du kannst es direkt bekommen und auf die Indirection verzichten:

1
2
3
4
5
6
7
for var in wordlist; do
  # eval 'array2=("${'"${var}"'[@]}")'
  typeset -a 'array2=("${'"${var}"'[@]}")'
  for x in ${array2[@]}; do
	echo "${x}"
	done
done

Mein for _a in "${!Arrays[@]}" bezieht sich auf meinen zweiten Vorschlag für Schritt a), wobei Arrays ein assoc. Array ist (was es nicht sein müsste).

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

Statt einzelne Zwischen-Arrays kann man auch gleich ein einzelnes bauen (Idee: von geirha in bash@freenode):

1
2
3
4
5
6
source FILE
typeset -a "A=( $(printf '"${%s[@]}" ' "${!TEST_@}") )"
for a in "${A[@]}"
do
        echo "$a"
done

:

Antworten |