¿Es posible recorrer tuplas en bash?
Como ejemplo, sería genial si lo siguiente funcionara:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
¿Existe alguna solución que de alguna manera me permita recorrer tuplas?
¿Es posible recorrer tuplas en bash?
Como ejemplo, sería genial si lo siguiente funcionara:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
¿Existe alguna solución que de alguna manera me permita recorrer tuplas?
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5
Acerca de este uso de set
(de man builtins
):
Los argumentos restantes después del procesamiento de la opción se tratan como valores para los parámetros posicionales y se asignan, en orden, a $ 1, $ 2, ... $ n
La IFS=","
fija el separador de campo por lo que cada $i
consigue segmentado en $1
y $2
correctamente.
A través de este blog .
Editar: versión más correcta, como lo sugiere @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
IFS
debe guardarse y restablecerse a su valor original si se ejecuta en la línea de comando. Además, el nuevo IFS
se puede configurar una vez, antes de que se ejecute el ciclo, en lugar de cada iteración.
set -- $i
IFS
, sólo se establece que para el set
comando: for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Edite su respuesta: si todos los lectores eligieran solo una de las soluciones enumeradas, no tiene sentido tener que leer el historial de desarrollo completo. ¡Gracias por este genial truco!
tuples="a,1 b,2 c,3"
y coloco IFS=','
como en la versión editada, y en lugar de c,3 e,5
usarlo $tuples
, no se imprime nada bien. Pero en cambio, si pongo IFS=','
justo después de la do
palabra clave en el ciclo for, funciona bien cuando se usan $tuples
valores además de literales. Solo pensé que valía la pena decirlo.
IFS
para dividir iteraciones. es decir, si recorre una matriz como arr=("c,3" "e,5")
y la coloca IFS
antes del bucle for, el valor de $i
será solo c
y e
, se dividirá 3
y, 5
por set
lo tanto , no analizará correctamente porque $i
no tendrá nada que analizar. Esto significa que si los valores para iterar no están alineados, IFS
deben colocarse dentro del ciclo y el valor externo debe respetar el separador previsto para la variable sobre la que iterar. En los casos en $tuples
que debería ser simplemente IFS=
cuál es el predeterminado y se divide en espacios en blanco.
Creo que esta solución es un poco más limpia que las otras que se han enviado, h / t a esta guía de estilo de bash para ilustrar cómo se puede usar read para dividir cadenas en un delimitador y asignarlas a variables individuales.
for i in c,3 e,5; do
IFS=',' read item1 item2 <<< "${i}"
echo "${item1}" and "${item2}"
done
Según la respuesta dada por @ eduardo-ivanec sin configurar / restablecer IFS
, uno podría simplemente hacer:
for i in "c 3" "e 5"
do
set -- $i
echo $1 and $2
done
La salida:
c and 3
e and 5
Utilice una matriz asociativa (también conocida como diccionario / 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 +.
Si necesita triples en lugar de pares, puede utilizar el enfoque más general:
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
GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)
, que se instaló a través de brew, por lo que YMMV.
GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
desde Ubuntu 14.04 dentro del contenedor Docker.
-A
nosotros tenemos -a
.
declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
etc. Aún no lo he probado en la terminal, pero creo que debería funcionar.
c=('a' 'c')
n=(3 4 )
for i in $(seq 0 $((${#c[*]}-1)))
do
echo ${c[i]} ${n[i]}
done
A veces puede ser más útil.
Para explicar el ugly
parte, como se indica en los comentarios:
seq 0 2 produce la secuencia de números 0 1 2. $ (cmd) es la sustitución de comandos, por lo que para este ejemplo la salida de seq 0 2
, que es la secuencia numérica. Pero, ¿cuál es el límite superior, el $((${#c[*]}-1))
?
$ ((algo)) es expansión aritmética, por lo que $ ((3 + 4)) es 7, etc. Nuestra expresión es ${#c[*]}-1
, entonces algo: 1. Bastante simple, si sabemos qué${#c[*]}
es.
c es una matriz, c [*] es solo la matriz completa, $ {# c [*]} es el tamaño de la matriz, que es 2 en nuestro caso. Ahora revertimos todo: for i in $(seq 0 $((${#c[*]}-1)))
es for i in $(seq 0 $((2-1)))
es for i in $(seq 0 1)
es for i in 0 1
. Porque el último elemento de la matriz tiene un índice que es la longitud de la matriz - 1.
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
Usando GNU Parallel:
parallel echo {1} and {2} ::: c e :::+ 3 5
O:
parallel -N2 echo {1} and {2} ::: c 3 e 5
O:
parallel --colsep , echo {1} and {2} ::: c,3 e,5
gnu parallel
brew install parallel
Usando printf
en una sustitución de proceso:
while read -r k v; do
echo "Key $k has value: $v"
done < <(printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3')
Key key1 has value: val1
Key key2 has value: val2
Key key3 has value: val3
Lo anterior requiere bash
. Si bash
no se está utilizando, utilice una canalización simple:
printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3' |
while read -r k v; do echo "Key $k has value: $v"; done
do echo $key $value
done < file_discriptor
por ejemplo:
$ 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
Un poco más complicado, pero puede 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
Pero, ¿qué pasa si la tupla es mayor que el k / v que puede contener una matriz asociativa? ¿Y si son 3 o 4 elementos? Se podría ampliar este concepto:
###---------------------------------------------------
### 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
Entonces la salida se vería así:
$ ./assoc-array-tinkering.sh
Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
Y el número de elementos ahora es ilimitado. Sin buscar votos; solo pensando en voz alta. REF1 , REF2
En los casos en que mis definiciones de tupla son más complejas, prefiero tenerlas en un heredoc:
while IFS=", " read -ra arr; do
echo "${arr[0]} and ${arr[1]}"
done <<EOM
c, 3
e, 5
EOM
Esto combina bucle sobre las líneas de un heredoc con dividir las líneas en algún carácter de separación deseado .