289 Stimmen

Schleifen über Arrays, Drucken von Index und Wert

Ich möchte etwas in dieser Art machen:

foo=( )
foo[0]="bar"
foo[35]="baz"
for((i=0;i<${#foo[@]};i++))
do
    echo "$i: ${foo[$i]}"
done
# Output:
# 0: bar
# 1: 

Dann habe ich versucht, es mit for in in einer Schleife zu verarbeiten:

foo=( )
foo[0]="bar"
foo[35]="baz"
for i in ${foo[@]}
do
    echo "?: $i"
done
# Output:
# ?: bar
# ?: naz

aber hier kenne ich den Indexwert nicht.

Ich weiß, dass Sie etwas wie

foo=( )
foo[0]="bar"
foo[35]="baz"
declare -p foo
# Output:
# declare -a foo='([0]="bar" [35]="baz")'

aber kann man das nicht auch anders machen?

494voto

glenn jackman Punkte 221248

Sie finden die Array-Schlüssel mit "${!foo[@]}" ( Referenz ), also:

for i in "${!foo[@]}"; do 
  printf "%s\t%s\n" "$i" "${foo[$i]}"
done

Das bedeutet, dass die Indizes in $i während der Zugriff auf die Elemente selbst über ${foo[$i]}

44voto

Eyal Ch Punkte 8554

Können Sie immer den Iterationsparameter verwenden:

ITER=0
for I in ${FOO[@]}
do  
    echo ${I} ${ITER}
    ITER=$(expr $ITER + 1)
done

31voto

Aruy Aruy Punkte 425
INDEX=0
for i in $list; do 
    echo ${INDEX}_$i
    let INDEX=${INDEX}+1
done

16voto

F. Hauri Punkte 57640

Einfach eine Zeile Trick für Dumping Array

Ich habe einen Wert mit Leerzeichen hinzugefügt:

foo=([12]="bar" [42]="foo bar baz" [35]="baz")

Für einen schnellen Dump von bash Arrays oder assoziative Anordnungen Ich benutze

Dieser einzeilige Befehl:

paste <(printf "%s\n" "${!foo[@]}") <(printf "%s\n" "${foo[@]}")

Wird gerendert:

12  bar
35  baz
42  foo bar baz

Erläutert

  • printf "%s\n" "${!foo[@]}" druckt alle Tasten getrennt durch ein Zeilenumbruch ,
  • printf "%s\n" "${foo[@]}" druckt alle Werte getrennt durch ein Zeilenumbruch ,
  • paste <(cmd1) <(cmd2) fügt die Ausgabe von cmd1 y cmd2 Zeile für Zeile.

Mahnungen

Dies könnte folgendermaßen eingestellt werden -d Schalter:

paste -d : <(printf "%s\n" "${!foo[@]}") <(printf "%s\n" "${foo[@]}")
12:bar
35:baz
42:foo bar baz

oder sogar:

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "'%s'\n" "${foo[@]}")
foo[12]='bar'
foo[35]='baz'
foo[42]='foo bar baz'

Assoziatives Array funktioniert genauso:

declare -A bar=([foo]=snoopy [bar]=nice [baz]=cool [foo bar]='Hello world!')
paste -d = <(printf "bar[%s]\n" "${!bar[@]}") <(printf '"%s"\n' "${bar[@]}")
bar[foo bar]="Hello world!"
bar[foo]="snoopy"
bar[bar]="nice"
bar[baz]="cool"

Problem mit Zeilenumbrüche oder Sonderzeichen

Leider gibt es mindestens eine Bedingung, unter der dies nicht mehr funktioniert: wenn die Variable einen Zeilenumbruch enthält:

foo[17]=$'There is one\nnewline'

Befehl paste wird zeilenweise zusammengeführt, was zu einer falschen Ausgabe führt:

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "'%s'\n" "${foo[@]}")
foo[12]='bar'
foo[17]='There is one
foo[35]=newline'
foo[42]='baz'
='foo bar baz'

Für diese Arbeit könnten Sie Folgendes verwenden %q 代わりに %s in zweiter Linie printf Befehl (und Whipe-Zitat):

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "%q\n" "${foo[@]}")

Wird perfekt gemacht ( und wiederverwendbar! ):

foo[12]=bar
foo[17]=$'There is one\nnewline'
foo[35]=baz
foo[42]=foo\ bar\ baz

Von man bash :

          %q     causes  printf  to output the corresponding argument in a
                 format that can be reused as shell input.

Oder durch Verwendung einer Funktion:

dumpArray() {
    local -n _ary=$1
    local _idx
    local -i _idlen=0
    for _idx in "${!_ary[@]}"; do
        _idlen=" ${#_idx} >_idlen ? ${#_idx} : _idlen "
    done
    for _idx in "${!_ary[@]}"; do
        printf "%-*s: %s\n" "$_idlen" "$_idx" \
            "${_ary["$_idx"]//$'\n'/$'\n\e['${_idlen}C: }"
    done
}

Dann jetzt:

dumpArray foo
12: bar
17: There is one
  : newline
35: baz
42: foo bar baz

dumpArray bar
foo    : snoopy
bar    : nice
baz    : cool
foo bar: Hello world!

Über die Ausgabe im UTF-8-Format

Von UTF-8 Zeichenkettenlänge und fügte hinzu:

strU8DiffLen() { local chLen=${#1} LANG=C LC_ALL=C;return $((${#1}-chLen));}

Dann

dumpArray() {
    local -n _ary=$1
    local _idx
    local -i _idlen=0
    for _idx in "${!_ary[@]}"; do
        _idlen=" ${#_idx} >_idlen ? ${#_idx} : _idlen "
    done
    for _idx in "${!_ary[@]}"; do
        strU8DiffLen "$_idx"
        printf "%-*s: %s\n" $(($?+$_idlen)) "$_idx" \
            "${_ary["$_idx"]//$'\n'/$'\n\e['${_idlen}C: }"
    done
}

Demo:

foo=([12]="bar" [42]="foo bar baz" [35]="baz")
declare -A bar=([foo]=snoopy [bar]=nice [baz]=cool [foo bar]='Hello world!')

foo[17]=$'There is one\nnewline'
LANG=fr.UTF-8 printf -v bar[déjà]  $'%(%a %d %b\n%Y\n%T)T' -1

dumpArray bar
déjà   : ven 24 déc
       : 2021
       : 08:36:05
foo    : snoopy
bar    : nice
baz    : cool
foo bar: Hello world!

dumpArray foo
12: bar
17: There is one
  : newline
35: baz
42: foo bar baz

7voto

mattmc3 Punkte 16673

In bash 4 können Sie assoziative Arrays verwenden:

declare -A foo
foo[0]="bar"
foo[35]="baz"

# for Zsh, change this to: for key in "${(k)foo[@]}"
for key in "${!foo[@]}"
do
    echo "key: $key, value: ${foo[$key]}"
done

# output
# $ key: 0, value bar.
# $ key: 35, value baz.

In bash 3 funktioniert dies (auch in zsh):

map=( )
map+=("0:bar")
map+=("35:baz")

for keyvalue in "${map[@]}" ; do
    key=${keyvalue%%:*}
    value=${keyvalue#*:}
    echo "key: $key, value $value."
done

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X