Перебирать кортежи в bash?

Можно ли закодировать кортежи в bash?

В качестве примера было бы здорово, если бы работало следующее:

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

Есть ли способ обхода, который каким-то образом позволяет мне перебирать кортежи?

Ответ 1

$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5

Об этом использовании set (от 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

Ответ 2

Я считаю, что это решение немного чище, чем другие, которые были отправлены, h/t в этот bash стиль для иллюстрации как read может использоваться для разделения строк на разделителе и назначения их отдельным переменным.

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}"
    echo "${item1}" and "${item2}"
done

Ответ 3

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

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

Иногда бывает удобнее.

Чтобы объяснить часть 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) is for i in 0 1. Поскольку последний элемент в массиве имеет индекс, который является длиной массива - 1.

Ответ 4

$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done
c and 3
e and 5

Ответ 5

Использование GNU Parallel:

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

Или:

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

Или:

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

Ответ 6

Немного больше, но может быть полезно:

a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done