2012-03-15 3 views
50

Можно ли перебрать кортежи в bash?Перебирать кортежи в bash?

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

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

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

+2

Исходя из питона фона это очень полезный вопрос на самом деле! –

+3

глядя на это четыре года спустя, я задаюсь вопросом, нет ли лучшего способа сделать это. О, мой бог. – Giszmo

ответ

58
$ 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, ... $ п

IFS="," устанавливает разделитель полей так каждый $i получает сегментирован в $1 и $2 правильно.

Via this blog.

Edit: более правильный вариант, как это было предложено @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 
+7

Nice - просто хочу указать, что 'IFS' должен быть сохранен и сброшен до его первоначального значения, если он запущен в командной строке. Кроме того, новый 'IFS' может быть установлен один раз, прежде чем цикл будет работать, а не каждая итерация. – Devourant

+1

@SLACEDIAMOND: спасибо, я включил ваши предложения. –

+0

В случае, если какой-либо из $ i начинается с дефиса, безопаснее 'set - $ i' –

0

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

a='((c,3), (e,5))' 
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done 
4
$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done 
c and 3 
e and 5 
6
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))) is for i in $(seq 0 $((2-1))) is for i in $(seq 0 1) is for i in 0 1. Поскольку последний элемент массива имеет индекс, который является длиной массива - 1.

+1

вы должны сделать 'for i в $ (seq 0 $ (($ # c [*]} - 1))); do [...] ' – reox

+0

@reox: Готово. Спасибо. –

+1

Ничего себе, это побеждает награду «Самый ужасный гроздь произвольных персонажей, которые я видел сегодня». Кто-нибудь хочет объяснить, что именно делает эта мерзость? Я потерялся на знаке хеша ... – koniiiik

10

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

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

Использование 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 
+1

Нет любви к этому? хорошо сделанный * me * преодолеть инерцию и установить 'gnu parallel' – javadba

+1

' brew install parallel' – javadba

0

Используйте ассоциативный массив (также известный как словарь/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 

Работы для 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