Bash'de tuple'ları döngülemek mümkün mü?
Örnek olarak, aşağıdakilerin işe yaraması harika olurdu:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Bir şekilde demetlerin üzerinden geçmeme izin veren bir çözüm var mı?
Bash'de tuple'ları döngülemek mümkün mü?
Örnek olarak, aşağıdakilerin işe yaraması harika olurdu:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Bir şekilde demetlerin üzerinden geçmeme izin veren bir çözüm var mı?
Yanıtlar:
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5
Bu set
(kimden man builtins
) kullanımı hakkında:
Seçenek işlemeden sonra kalan bağımsız değişkenler, konumsal parametreler için değerler olarak kabul edilir ve sırasıyla $ 1, $ 2, ... $ n olarak atanır
IFS=","
Her sözüm alan ayırıcı setleri $i
bölünür alır $1
ve$2
düzgün.
Via bu blog .
Düzenleme: @SLACEDIAMOND tarafından önerildiği gibi daha doğru sürüm:
$ 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
komut satırında çalıştırılırsa kaydedilmesi ve orijinal değerine sıfırlanması gerektiğini belirtmek istiyorum . Ayrıca yeni IFS
, her yinelemeden ziyade döngü çalışmadan önce bir kez ayarlanabilir.
set -- $i
IFS
, sadece ayarlıyorum set
komuta: for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Lütfen cevabınızı düzenleyin: Tüm okuyucular listelenen çözümlerden yalnızca birini seçerse, tam geliştirme geçmişini okumak zorunda kalmanın bir anlamı yoktur. Bu harika numara için teşekkürler!
tuples="a,1 b,2 c,3"
edip koyarsam IFS=','
ve c,3 e,5
kullanmak yerine $tuples
hiç iyi yazdırmaz. Ancak bunun yerine , for döngüsünde anahtar sözcüğün IFS=','
hemen sonrasına koyarsam do
, $tuples
hem literal değerleri hem de kullanırken iyi çalışır . Söylemeye değer olduğunu düşündüm.
IFS
yinelemeleri bölmek için kullanılmasıdır . Eğer böyle bir diziye üzerinde döngü halinde yani arr=("c,3" "e,5")
ve koyun IFS
döngü için önce, değeri $i
sadece olacak c
ve e
bu uzak bölecektir 3
ve 5
böylece set
çünkü doğru ayrıştırma olacak $i
ayrıştırma şey olmaz. Bu, yinelenecek değerler satır içi değilse IFS
, döngünün içine yerleştirilmesi gerektiği ve dış değerin, değişkenin yinelemesi için amaçlanan ayırıcıya uyması gerektiği anlamına gelir. Böyle durumlarda , varsayılan olan ve boşluklara bölünen $tuples
basit olmalıdır IFS=
.
Bu çözümün sunulan diğer çözümlerden biraz daha temiz olduğuna inanıyorum, bu bash stili kılavuza, okumanın bir sınırlayıcıda dizeleri ayırmak ve bunları ayrı değişkenlere atamak için nasıl kullanılabileceğini göstermek için h / t .
for i in c,3 e,5; do
IFS=',' read item1 item2 <<< "${i}"
echo "${item1}" and "${item2}"
done
@ Eduardo-ivanec tarafından, 'yi ayarlamadan / sıfırlamadan verilen cevaba dayanarak, IFS
basitçe şunları yapabilirsiniz:
for i in "c 3" "e 5"
do
set -- $i
echo $1 and $2
done
Çıktı:
c and 3
e and 5
İlişkilendirilebilir dizi kullanın (sözlük / hashMap olarak da bilinir):
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 + için çalışır.
Çiftler yerine üçlülere ihtiyacınız varsa, daha genel yaklaşımı kullanabilirsiniz:
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)
brew aracılığıyla kurulan Mac'te işe yaramadı, bu yüzden YMMV.
GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
docker container içinde Ubuntu 14.04'ten çalıştı.
-A
var -a
.
declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
vb. Henüz terminalde denemedim, ama işe yaraması gerektiğini düşünüyorum.
c=('a' 'c')
n=(3 4 )
for i in $(seq 0 $((${#c[*]}-1)))
do
echo ${c[i]} ${n[i]}
done
Bazen daha kullanışlı olabilir.
ugly
Yorumlarda belirtildiği gibi kısmı açıklamak için :
seq 0 2 , 0 1 2 sayılarının dizisini üretir. $ (cmd), komut ikamesidir, bu nedenle bu örnek için çıktısı seq 0 2
, sayı dizisi olan. Ama üst sınır $((${#c[*]}-1))
nedir?
$ ((somthing)) aritmetik açılımdır, yani $ (((3 + 4)) 7'dir vs. Bizim İfademiz ${#c[*]}-1
, yani bir şey - 1. Ne ${#c[*]}
olduğunu bilirsek oldukça basit .
c bir dizidir, c [*] yalnızca tüm dizidir, $ {# c [*]} bizim durumumuzda 2 olan dizinin boyutudur. Şimdi herşeyi geri rulo: for i in $(seq 0 $((${#c[*]}-1)))
olduğunu for i in $(seq 0 $((2-1)))
olduğunu for i in $(seq 0 1)
olduğunu for i in 0 1
. Çünkü dizideki son eleman, Dizi - 1'in uzunluğu olan bir dizine sahiptir.
for i in $(seq 0 $(($#c[*]}-1))); do [...]
GNU Parallel Kullanımı:
parallel echo {1} and {2} ::: c e :::+ 3 5
Veya:
parallel -N2 echo {1} and {2} ::: c 3 e 5
Veya:
parallel --colsep , echo {1} and {2} ::: c,3 e,5
gnu parallel
brew install parallel
printf
Bir işlem ikamesinde kullanmak :
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
Yukarıdakiler gerektirir bash
. Kullanılmıyorsa bash
, basit ardışık düzen kullanın:
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
Örneğin:
$ 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
Biraz daha karmaşık, ancak faydalı olabilir:
a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
Peki ya demet, ilişkilendirilebilir dizinin tutabileceği k / v değerinden büyükse? Ya 3 veya 4 elementse? Bu kavramı genişletebiliriz:
###---------------------------------------------------
### 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
Ardından çıktı şuna benzer görünecektir:
$ ./assoc-array-tinkering.sh
Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
Ve eleman sayısı artık sınırsızdır. Oy aramamak; sadece yüksek sesle düşünüyorum. REF1 , REF2
Demet tanımlarımın daha karmaşık olduğu durumlarda, bunları bir yorumda bulundurmayı tercih ederim:
while IFS=", " read -ra arr; do
echo "${arr[0]} and ${arr[1]}"
done <<EOM
c, 3
e, 5
EOM
Bu birleştirir bir yorumlu metin satırları üzerinde döngü ile bölünme bazı istenen ayırma karakterde hatları .