Czy można zapętlić krotki w bash?
Na przykład byłoby świetnie, gdyby zadziałały:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Czy istnieje obejście, które w jakiś sposób pozwala mi zapętlić krotki?
Czy można zapętlić krotki w bash?
Na przykład byłoby świetnie, gdyby zadziałały:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Czy istnieje obejście, które w jakiś sposób pozwala mi zapętlić krotki?
Odpowiedzi:
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5
O tym wykorzystaniu set
(od man builtins
):
Wszelkie argumenty pozostałe po przetworzeniu opcji są traktowane jako wartości parametrów pozycyjnych i są przypisywane kolejno do $ 1, $ 2, ... $ n
Te IFS=","
zestawy separator pól więc każdy $i
dostaje podzielony $1
i$2
prawidłowo.
Za pośrednictwem tego bloga .
Edycja: bardziej poprawna wersja, zgodnie z sugestią @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
powinien zostać zapisany i zresetowany do pierwotnej wartości, jeśli jest to uruchamiane w wierszu poleceń. Ponadto nowy IFS
może być ustawiony raz, przed uruchomieniem pętli, a nie przy każdej iteracji.
set -- $i
IFS
, tylko ustawić go na set
komendę: for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Edytuj swoją odpowiedź: Jeśli wszyscy czytelnicy wybiorą tylko jedno z wymienionych rozwiązań, nie ma sensu czytać pełnej historii rozwoju. Dzięki za tę fajną sztuczkę!
tuples="a,1 b,2 c,3"
i wstawię IFS=','
tak, jak w redagowanej wersji, a zamiast tego c,3 e,5
użyć $tuples
, wcale nie drukuje się dobrze. Ale zamiast tego, jeśli wstawię w pętli for IFS=','
tuż po do
słowie kluczowym, działa to dobrze, gdy używam $tuples
wartości litteral. Pomyślałem, że warto to powiedzieć.
IFS
do dzielenia iteracji. tj Jeżeli pętla na tablicy jak arr=("c,3" "e,5")
i postawić IFS
przed pętli, wartość $i
będzie tylko c
i e
będzie podzielony dala 3
i 5
tak set
nie będzie analizować poprawnie, ponieważ $i
nie będą mieli nic do analizowania. Oznacza to, że jeśli wartości do iteracji nie są wstawiane, to IFS
należy umieścić wewnątrz pętli, a wartość zewnętrzna powinna uwzględniać zamierzony separator zmiennej, po której ma być iterowana. W takich przypadkach $tuples
powinno być po prostu IFS=
to, co jest domyślne i dzieli się na białe znaki.
Uważam, że to rozwiązanie jest trochę czystsze niż inne, które zostały przesłane, h / t do tego przewodnika po stylu basha, aby zilustrować, jak można użyć odczytu do dzielenia ciągów na separatorze i przypisywania ich do poszczególnych zmiennych.
for i in c,3 e,5; do
IFS=',' read item1 item2 <<< "${i}"
echo "${item1}" and "${item2}"
done
W oparciu o odpowiedź udzieloną przez @ eduardo-ivanec bez ustawiania / resetowania IFS
, można po prostu zrobić:
for i in "c 3" "e 5"
do
set -- $i
echo $1 and $2
done
Wyjście:
c and 3
e and 5
Użyj tablicy asocjacyjnej (znanej również jako Dictionary / 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
Działa dla bash4.0 +.
Jeśli potrzebujesz trójek zamiast par, możesz zastosować bardziej ogólne podejście:
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)
, który został zainstalowany przez brew, więc YMMV.
GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
Ubuntu 14.04 w kontenerze docker.
-A
mamy -a
.
declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
itp. Nie próbowałem jeszcze tego w terminalu, ale myślę, że powinno działać.
c=('a' 'c')
n=(3 4 )
for i in $(seq 0 $((${#c[*]}-1)))
do
echo ${c[i]} ${n[i]}
done
Czasami może być bardziej przydatne.
Aby wyjaśnić ugly
część, jak zaznaczono w komentarzach:
seq 0 2 tworzy sekwencję liczb 0 1 2. $ (cmd) jest podstawieniem poleceń, więc w tym przykładzie wynikiem seq 0 2
jest sekwencja liczb. Ale jaka jest górna granica $((${#c[*]}-1))
?
$ ((coś)) jest rozwinięciem arytmetycznym, więc $ ((3 + 4)) to 7 itd. Nasze wyrażenie to ${#c[*]}-1
, więc coś - 1. Całkiem proste, jeśli wiemy, co ${#c[*]}
to jest.
c to tablica, c [*] to po prostu cała tablica, $ {# c [*]} to rozmiar tablicy, który w naszym przypadku wynosi 2. Teraz cofamy wszystko: for i in $(seq 0 $((${#c[*]}-1)))
jest for i in $(seq 0 $((2-1)))
to for i in $(seq 0 1)
jest for i in 0 1
. Ponieważ ostatni element tablicy ma indeks, który jest długością tablicy Array - 1.
for i in $(seq 0 $(($#c[*]}-1))); do [...]
Używanie GNU równolegle:
parallel echo {1} and {2} ::: c e :::+ 3 5
Lub:
parallel -N2 echo {1} and {2} ::: c 3 e 5
Lub:
parallel --colsep , echo {1} and {2} ::: c,3 e,5
gnu parallel
brew install parallel
Stosowanie printf
w zastępowaniu procesów:
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
Powyższe wymaga bash
. Jeśli bash
nie jest używany, użyj prostego potoku:
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
na przykład:
$ 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
Trochę bardziej zaangażowany, ale może być przydatny:
a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
Ale co, jeśli krotka jest większa niż k / v, które może pomieścić tablica asocjacyjna? A jeśli to 3 lub 4 elementy? Można by rozwinąć tę koncepcję:
###---------------------------------------------------
### 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
Wtedy wynik wyglądałby mniej więcej tak:
$ ./assoc-array-tinkering.sh
Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
A liczba elementów jest teraz nieograniczona. Nie szukam głosów; po prostu głośno myślę. REF1 , REF2
W przypadkach, gdy moje definicje krotek są bardziej złożone, wolę mieć je w heredoc:
while IFS=", " read -ra arr; do
echo "${arr[0]} and ${arr[1]}"
done <<EOM
c, 3
e, 5
EOM
Łączy to zapętlanie linii heredoc z dzieleniem linii na jakimś pożądanym znaku oddzielającym .