在bash中循环元组?

Fra*_*ank 75 bash for-loop

是否有可能在bash中循环元组?

作为一个例子,如果以下工作将会很好:

for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Run Code Online (Sandbox Code Playgroud)

是否有一种解决方法,以某种方式让我循环元组?

Edu*_*nec 78

$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5
Run Code Online (Sandbox Code Playgroud)

关于set(from man builtins)的这种用法:

选项处理后剩余的任何参数都被视为位置参数的值,并按顺序分配给$ 1,$ 2,... $ n

IFS=","套字段分隔符所以每天$i被分成$1$2正确.

通过这个博客.

编辑:更正确的版本,如@SLACEDIAMOND所示:

$ OLDIFS=$IFS; IFS=','; for i in c,3 e,5; do set -- $i; echo $1 and $2; done; IFS=$OLDIFS
c and 3
e and 5
Run Code Online (Sandbox Code Playgroud)

  • 很好 - 只是想指出`IFS`应该保存并重置为其原始值,如果这是在命令行上运行.此外,新的`IFS`可以在循环运行之前设置一次,而不是每次迭代. (7认同)
  • 而不是保存`IFS`,只为`set`命令设置它:`for i in c,3 e,5; 做 IFS="," 设置 -- $i; 回声 $1 和 $2;完成`。请编辑您的答案:如果所有读者只选择列出的解决方案之一,那么阅读完整的开发历史是没有意义的。感谢这个很酷的技巧! (2认同)
  • 如果我声明 `tuples="a,1 b,2 c,3"` 并把 `IFS=','` 放在编辑版本中,而不是 `c,3 e,5` 使用 `$tuples`根本打印不好。但是,如果我将 `IFS=','` 放在 for 循环中的 `do` 关键字后面,那么在使用 `$tuples` 以及文字值时效果很好。只是觉得值得一说。 (2认同)

Gra*_*ies 16

我相信这个解决方案比已提交的其他解决方案更清晰,而不是这个 bash样式指南,用于说明如何使用read来在分隔符处拆分字符串并将它们分配给单个变量.

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}"
    echo "${item1}" and "${item2}"
done
Run Code Online (Sandbox Code Playgroud)


Vas*_*kov 8

使用关联数组(也称为字典/ hashMap):

declare -A pairs=(
  [c]=3
  [e]=5
)
for key in "${!pairs[@]}"; do
  value="${pairs[$key]}"
  echo "key is $key and value is $value"
done
Run Code Online (Sandbox Code Playgroud)

适用于bash4.0 +.


如果您觉得有一天可能需要三倍而不是成对,您可以使用更通用的方法:

animals=(dog cat mouse)
declare -A sound=(
  [dog]=barks
  [cat]=purrs
  [mouse]=cheeps
)
declare -A size=(
  [dog]=big
  [cat]=medium
  [mouse]=small
)
for animal in "${animals[@]}"; do
  echo "$animal ${sound[$animal]} and it is ${size[$animal]}"
done
Run Code Online (Sandbox Code Playgroud)


use*_*own 7

c=('a' 'c')
n=(3    4 )

for i in $(seq 0 $((${#c[*]}-1)))
do
    echo ${c[i]} ${n[i]}
done
Run Code Online (Sandbox Code Playgroud)

有时可能会更方便.

ugly如评论中所述,解释该部分:

seq 0 2产生数字序列0 1 2. $(cmd)是命令替换,因此对于此示例输出seq 0 2,即数字序列.但是上限是$((${#c[*]}-1))什么?

$((somthing))是算术扩展,所以$((3 + 4))是7等等.我们的表达式是${#c[*]}-1这样的东西 - 1.非常简单,如果我们知道什么${#c[*]}是.

c是一个数组,c [*]只是整个数组,$ {#c [*]}是数组的大小,在我们的例子中是2.现在我们推出的一切回:for i in $(seq 0 $((${#c[*]}-1)))for i in $(seq 0 $((2-1)))for i in $(seq 0 1)for i in 0 1.因为数组中的最后一个元素的索引是Array的长度 - 1.

  • 你应该做`for i in $(seq 0 $(($#c[*]}-1))); 做[...]` (2认同)
  • 哇,这赢得了“我今天见过的最丑的一群任意角色”奖。有人愿意解释一下这个可憎的东西到底是做什么的吗?我在哈希标志处迷路了...... (2认同)

tod*_*dsm 7

但是如果元组大于关联数组可以容纳的 k/v 该怎么办?如果是 3 或 4 个元素怎么办?人们可以扩展这一概念:

###---------------------------------------------------
### VARIABLES
###---------------------------------------------------
myVars=(
    'ya1,ya2,ya3,ya4'
    'ye1,ye2,ye3,ye4'
    'yo1,yo2,yo3,yo4'
    )


###---------------------------------------------------
### MAIN PROGRAM
###---------------------------------------------------
### Echo all elements in the array
###---
printf '\n\n%s\n' "Print all elements in the array..."
for dataRow in "${myVars[@]}"; do
    while IFS=',' read -r var1 var2 var3 var4; do
        printf '%s\n' "$var1 - $var2 - $var3 - $var4"
    done <<< "$dataRow"
done
Run Code Online (Sandbox Code Playgroud)

然后输出将类似于:

$ ./assoc-array-tinkering.sh 

Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
Run Code Online (Sandbox Code Playgroud)

并且元素的数量现在没有限制。不寻求选票;只是大声思考。参考1 ,参考2


kev*_*kev 6

$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done
c and 3
e and 5
Run Code Online (Sandbox Code Playgroud)


MZH*_*ZHm 6

根据@ eduardo-ivanec给出的答案,无需设置/重置IFS,只需执行以下操作:

for i in "c 3" "e 5"
do
    set -- $i
    echo $1 and $2
done
Run Code Online (Sandbox Code Playgroud)

输出:

c and 3
e and 5
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,这种方法比公认的和最受欢迎的方法简单得多。有什么理由不这样做而不是@Eduardo Ivanec 建议的方式? (3认同)
  • @spurra,如果默认分隔符(空格、制表符或换行符)由于某种原因不适合您,您需要使用 Eduardo 的答案(https://bash.cyberciti.biz/guide/$IFS) (2认同)