循环遍历数组,打印索引和值

Tyi*_*ilo 158 bash

我想做这样的事情:

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

然后我尝试使用in for循环它:

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

但在这里我不知道索引值.

我知道你可以这样

foo=( )
foo[0]="bar"
foo[35]="baz"
declare -p foo
# Output:
# declare -a foo='([0]="bar" [35]="baz")'
Run Code Online (Sandbox Code Playgroud)

但是,你不能用另一种方式吗?

gle*_*man 271

你会发现数组键"${!foo[@]}"(引用),所以:

for i in "${!foo[@]}"; do 
  printf "%s\t%s\n" "$i" "${foo[$i]}"
done
Run Code Online (Sandbox Code Playgroud)

这意味着索引将在,$i而元素本身必须通过访问${foo[$i]}

  • 重要提示,在迭代时,以空格分隔的单词列表是_not_数组.把它换成`(abc)`把它转换成数组. (4认同)
  • 使用`[@]`和双引号意味着它不是"以空格分隔的单词列表".即使各个键包含空格,也可以获得实际数组键的列表. (4认同)

Eya*_* Ch 15

你总是可以使用迭代参数:

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

  • 现代bash中的(((ITER ++))) (4认同)
  • 不,不是"总是".哈希可以有"漏洞",这意味着并非所有数字都是索引.在您的示例中,$ {ITER}并不总是$ {I}的索引. (2认同)

F. *_*uri 12

转储数组的简单一行技巧

\n

我添加了一个带空格的值:

\n
foo=([12]="bar" [42]="foo bar baz" [35]="baz")\n
Run Code Online (Sandbox Code Playgroud)\n

为了快速转储数组或关联数组,我使用

\n

这一行命令:

\n
paste <(printf "%s\\n" "${!foo[@]}") <(printf "%s\\n" "${foo[@]}")\n
Run Code Online (Sandbox Code Playgroud)\n
\n

将渲染:

\n
12  bar\n35  baz\n42  foo bar baz\n
Run Code Online (Sandbox Code Playgroud)\n

解释

\n
    \n
  • printf "%s\\n" "${!foo[@]}"将打印所有由换行符分隔的
  • \n
  • printf "%s\\n" "${foo[@]}"将打印由换行符分隔的所有
  • \n
  • paste <(cmd1) <(cmd2)将逐行合并cmd1和的输出cmd2
  • \n
\n

调谐

\n

这可以通过-d开关来调整:

\n
paste -d : <(printf "%s\\n" "${!foo[@]}") <(printf "%s\\n" "${foo[@]}")\n12:bar\n35:baz\n42:foo bar baz\n
Run Code Online (Sandbox Code Playgroud)\n

甚至:

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

关联数组的工作方式相同:

\n
declare -A bar=([foo]=snoopy [bar]=nice [baz]=cool [foo bar]=\'Hello world!\')\npaste -d = <(printf "bar[%s]\\n" "${!bar[@]}") <(printf \'"%s"\\n\' "${bar[@]}")\nbar[foo bar]="Hello world!"\nbar[foo]="snoopy"\nbar[bar]="nice"\nbar[baz]="cool"\n
Run Code Online (Sandbox Code Playgroud)\n

换行符或特殊字符的问题

\n

不幸的是,至少有一个条件使它不再起作用:当变量确实包含换行符时:

\n
foo[17]=$\'There is one\\nnewline\'\n
Run Code Online (Sandbox Code Playgroud)\n

命令paste将逐行合并,因此输出将变得错误:

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

对于这项工作,您可以在第二个命令中使用%q而不是(和鞭打引用):%sprintf

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

将呈现完美(并且可重复使用!):

\n
foo[12]=bar\nfoo[17]=$\'There is one\\nnewline\'\nfoo[35]=baz\nfoo[42]=foo\\ bar\\ baz\n
Run Code Online (Sandbox Code Playgroud)\n

man bash

\n
\n
          %q     causes  printf  to output the corresponding argument in a\n                 format that can be reused as shell input.\n
Run Code Online (Sandbox Code Playgroud)\n
\n

通过使用函数:

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

那么现在:

\n
dumpArray foo\n12: bar\n17: There is one\n    newline\n35: baz\n42: foo bar baz\n\ndumpArray bar\nfoo    : snoopy\nbar    : nice\nbaz    : cool\nfoo bar: Hello world!\n
Run Code Online (Sandbox Code Playgroud)\n

关于UTF-8格式输出

\n

UTF-8 字符串 length,添加:

\n
strU8DiffLen() { local chLen=${#1} LANG=C LC_ALL=C;return $((${#1}-chLen));}\n
Run Code Online (Sandbox Code Playgroud)\n

然后

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

演示:

\n
foo=([12]="bar" [42]="foo bar baz" [35]="baz")\ndeclare -A bar=([foo]=snoopy [bar]=nice [baz]=cool [foo bar]=\'Hello world!\')\n\nfoo[17]=$\'There is one\\nnewline\'\nLANG=fr.UTF-8 printf -v bar[d\xc3\xa9j\xc3\xa0]  $\'%(%a %d %b\\n%Y\\n%T)T\' -1\n\ndumpArray bar\nd\xc3\xa9j\xc3\xa0   : ven 24 d\xc3\xa9c\n         2021\n         08:36:05\nfoo    : snoopy\nbar    : nice\nbaz    : cool\nfoo bar: Hello world!\n\ndumpArray foo\n12: bar\n17: There is one\n    newline\n35: baz\n42: foo bar baz\n
Run Code Online (Sandbox Code Playgroud)\n


小智 8

INDEX=0
for i in $list; do 
    echo ${INDEX}_$i
    let INDEX=${INDEX}+1
done
Run Code Online (Sandbox Code Playgroud)

  • 尽管此代码段可以解决问题,但是[包括说明](// meta.stackexchange.com/questions/114762/explaining-entrely-code-based-answers)确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。也请尽量不要在代码中加入解释性注释,这会降低代码和解释的可读性! (3认同)

mat*_*mc3 6

在 bash 4 中,您可以使用关联数组:

declare -A foo
foo[0]="bar"
foo[35]="baz"
for key in "${!foo[@]}"
do
    echo "key: $key, value: ${foo[$key]}"
done

# output
# $ key: 0, value bar.
# $ key: 35, value baz.
Run Code Online (Sandbox Code Playgroud)

在 bash 3 中,这有效(在 zsh 中也有效):

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

for keyvalue in "${map[@]}" ; do
    key=${keyvalue%%:*}
    value=${keyvalue#*:}
    echo "key: $key, value $value."
done
Run Code Online (Sandbox Code Playgroud)