É possível fazer um loop sobre tuplas no bash?
Por exemplo, seria ótimo se o seguinte funcionasse:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Existe uma solução alternativa que de alguma forma me permite fazer um loop sobre as tuplas?
Respostas:
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done c and 3 e and 5
Sobre este uso de
set
(deman builtins
):O
IFS=","
define o separador de campo para que todos sejam$i
segmentados$1
e$2
corretamente.Por meio deste blog .
Edit: versão mais correta, conforme sugerido por @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
fonte
IFS
deve ser salvo e redefinido para seu valor original se for executado na linha de comando. Além disso, o novoIFS
pode ser definido uma vez, antes da execução do loop, em vez de a cada iteração.set -- $i
IFS
apenas configurá-lo para oset
comando:for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Edite sua resposta: Se todos os leitores escolherem apenas uma das soluções listadas, não há sentido em ter que ler o histórico de desenvolvimento completo. Obrigado por este truque legal!tuples="a,1 b,2 c,3"
e colocoIFS=','
como na versão editada, e ao invés dec,3 e,5
usar$tuples
não imprime bem. Mas, em vez disso, se eu colocarIFS=','
logo após ado
palavra - chave no loop for, ele funcionará bem ao usar$tuples
valores literais. Achei que valia a pena dizer.IFS
para dividir iterações. ie Se você loop sobre uma matriz comoarr=("c,3" "e,5")
e colocarIFS
antes do loop, o valor$i
será apenasc
ee
, ele vai se separou3
e5
por issoset
não irá analisar corretamente porque$i
não vai ter nada para analisar. Isso significa que, se os valores a serem iterados não forem sequenciais, oIFS
deve ser colocado dentro do loop e o valor externo deve respeitar o separador pretendido para a variável a ser iterada. Nesse caso$tuples
, deve ser simplesmente oIFS=
que é padrão e se divide em espaços em branco.Eu acredito que esta solução é um pouco mais limpa do que as outras que foram enviadas, h / t para este guia de estilo bash para ilustrar como read pode ser usado para dividir strings em um delimitador e atribuí-las a variáveis individuais.
for i in c,3 e,5; do IFS=',' read item1 item2 <<< "${i}" echo "${item1}" and "${item2}" done
fonte
Baseado na resposta dada por @ eduardo-ivanec sem configurar / redefinir o
IFS
, pode-se simplesmente fazer:for i in "c 3" "e 5" do set -- $i echo $1 and $2 done
A saída:
fonte
Use array associativo (também conhecido como dicionário / 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
Funciona para bash4.0 +.
Se precisar de triplos em vez de pares, você pode usar a abordagem mais geral:
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
fonte
GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)
, que foi instalado via brew, então YMMV.GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
partir do Ubuntu 14.04 dentro do docker container.-A
nós temos-a
.declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
etc. Ainda não tentei no terminal, mas acho que deve funcionar.c=('a' 'c') n=(3 4 ) for i in $(seq 0 $((${#c[*]}-1))) do echo ${c[i]} ${n[i]} done
Às vezes pode ser mais útil.
Para explicar o
ugly
parte, conforme observado nos comentários:seq 0 2 produz a sequência de números 0 1 2. $ (cmd) é a substituição do comando, portanto, para este exemplo, a saída de
seq 0 2
, que é a sequência numérica. Mas qual é o limite superior, o$((${#c[*]}-1))
?$ ((somthing)) é a expansão aritmética, então $ ((3 + 4)) é 7 etc. Nossa Expressão é
${#c[*]}-1
, então algo - 1. Muito simples, se sabemos o que${#c[*]}
é.c é um array, c [*] é apenas o array inteiro, $ {# c [*]} é o tamanho do array que é 2 em nosso caso. Agora revertemos tudo:
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
. Porque o último elemento na matriz tem um índice que é o comprimento da matriz - 1.fonte
for i in $(seq 0 $(($#c[*]}-1))); do [...]
$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done c and 3 e and 5
fonte
Usando o GNU Parallel:
parallel echo {1} and {2} ::: c e :::+ 3 5
Ou:
parallel -N2 echo {1} and {2} ::: c 3 e 5
Ou:
parallel --colsep , echo {1} and {2} ::: c,3 e,5
fonte
gnu parallel
brew install parallel
Usando
printf
em uma substituição de processo:while read -r k v; do echo "Key $k has value: $v" done < <(printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3')
Acima requer
bash
. Sebash
não estiver sendo usado, use pipeline simples:printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3' | while read -r k v; do echo "Key $k has value: $v"; done
fonte
do echo $key $value done < file_discriptor
por exemplo:
$ while read key value; do echo $key $value ;done <<EOF > c 3 > e 5 > EOF c 3 e 5 $ echo -e 'c 3\ne 5' > file $ while read key value; do echo $key $value ;done <file c 3 e 5 $ echo -e 'c,3\ne,5' > file $ while IFS=, read key value; do echo $key $value ;done <file c 3 e 5
fonte
Um pouco mais envolvido, mas pode ser útil:
a='((c,3), (e,5))' IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
fonte
Mas e se a tupla for maior do que k / v que uma matriz associativa pode conter? E se forem 3 ou 4 elementos? Pode-se expandir este conceito:
###--------------------------------------------------- ### 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
Então, a saída seria algo como:
$ ./assoc-array-tinkering.sh Print all elements in the array... ya1 - ya2 - ya3 - ya4 ye1 - ye2 - ye3 - ye4 yo1 - yo2 - yo3 - yo4
E o número de elementos agora é ilimitado. Não procurando votos; apenas pensando em voz alta. REF1 , REF2
fonte
Nos casos em que minhas definições de tupla são mais complexas, prefiro tê-las em um heredoc:
while IFS=", " read -ra arr; do echo "${arr[0]} and ${arr[1]}" done <<EOM c, 3 e, 5 EOM
Isso combina o loop nas linhas de um heredoc com a divisão das linhas em algum caractere de separação desejado .
fonte