Loop over tuples in bash?

后端 未结 12 1421
说谎
说谎 2020-12-04 21:00

Is it possible to loop over tuples in bash?

As an example, it would be great if the following worked:

for (i,j) in ((c,3), (e,5)); do echo \"$i and $         


        
相关标签:
12条回答
  • 2020-12-04 21:19

    I believe this solution is a little cleaner than the others that have been submitted, h/t to this bash style guide for illustrating how read can be used to split strings at a delimiter and assign them to individual variables.

    for i in c,3 e,5; do 
        IFS=',' read item1 item2 <<< "${i}"
        echo "${item1}" and "${item2}"
    done
    
    0 讨论(0)
  • 2020-12-04 21:20
    c=('a' 'c')
    n=(3    4 )
    
    for i in $(seq 0 $((${#c[*]}-1)))
    do
        echo ${c[i]} ${n[i]}
    done
    

    Might sometimes be more handy.

    To explain the ugly part, as noted in the comments:

    seq 0 2 produces the sequence of numbers 0 1 2. $(cmd) is command substitution, so for this example the output of seq 0 2, which is the number sequence. But what is the upper bound, the $((${#c[*]}-1))?

    $((somthing)) is arithmetic expansion, so $((3+4)) is 7 etc. Our Expression is ${#c[*]}-1, so something - 1. Pretty simple, if we know what ${#c[*]} is.

    c is an array, c[*] is just the whole array, ${#c[*]} is the size of the array which is 2 in our case. Now we roll everything back: for i in $(seq 0 $((${#c[*]}-1))) is for i in $(seq 0 $((2-1))) is for i in $(seq 0 1) is for i in 0 1. Because the last element in the array has an index which is the length of the Array - 1.

    0 讨论(0)
  • 2020-12-04 21:21

    A bit more involved, but may be useful:

    a='((c,3), (e,5))'
    IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
    
    0 讨论(0)
  • 2020-12-04 21:21

    But what if the tuple is greater than the k/v that an associative array can hold? What if it's 3 or 4 elements? One could expand on this concept:

    ###---------------------------------------------------
    ### 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
    

    Then the output would look something like:

    $ ./assoc-array-tinkering.sh 
    
    Print all elements in the array...
    ya1 - ya2 - ya3 - ya4
    ye1 - ye2 - ye3 - ye4
    yo1 - yo2 - yo3 - yo4
    

    And the number of elements are now without limit. Not looking for votes; just thinking out loud. REF1, REF2

    0 讨论(0)
  • 2020-12-04 21:22

    Using GNU Parallel:

    parallel echo {1} and {2} ::: c e :::+ 3 5
    

    Or:

    parallel -N2 echo {1} and {2} ::: c 3 e 5
    

    Or:

    parallel --colsep , echo {1} and {2} ::: c,3 e,5
    
    0 讨论(0)
  • 2020-12-04 21:24

    Use associative array (also known as dictionary/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
    

    Works for bash4.0+.


    If you need triples instead of pairs, you can use the more general approach:

    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
    
    0 讨论(0)
提交回复
热议问题