Có thể lặp qua các bộ giá trị trong bash không?
Ví dụ, sẽ thật tuyệt nếu những điều sau đây hoạt động:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Có giải pháp nào cho phép tôi lặp lại các bộ giá trị không?
Có thể lặp qua các bộ giá trị trong bash không?
Ví dụ, sẽ thật tuyệt nếu những điều sau đây hoạt động:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Có giải pháp nào cho phép tôi lặp lại các bộ giá trị không?
Câu trả lời:
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5
Về việc sử dụng set
(từ man builtins
) này:
Bất kỳ đối số nào còn lại sau khi xử lý tùy chọn được coi là giá trị cho các tham số vị trí và được gán, theo thứ tự, cho $ 1, $ 2, ... $ n
Các IFS=","
bộ tách lĩnh vực như vậy mỗi $i
bị tách ra thành $1
và $2
chính xác.
Qua blog này .
Chỉnh sửa: phiên bản chính xác hơn, theo đề xuất của @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
nên được lưu và đặt lại về giá trị ban đầu nếu điều này được chạy trên dòng lệnh. Ngoài ra, cái mới IFS
có thể được đặt một lần, trước khi vòng lặp chạy, thay vì mỗi lần lặp lại.
set -- $i
IFS
, chỉ đặt nó cho set
lệnh: for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Vui lòng chỉnh sửa câu trả lời của bạn: Nếu tất cả người đọc chỉ chọn một trong các giải pháp được liệt kê, thì không có ý nghĩa gì khi phải đọc toàn bộ lịch sử phát triển. Cảm ơn vì thủ thuật thú vị này!
tuples="a,1 b,2 c,3"
và đặt IFS=','
như trong phiên bản đã chỉnh sửa, và thay vì c,3 e,5
sử dụng $tuples
thì nó không in tốt chút nào. Nhưng thay vào đó, nếu tôi IFS=','
chỉ đặt sau do
từ khóa trong vòng lặp for, nó sẽ hoạt động tốt khi sử dụng $tuples
cũng như các giá trị nhỏ. Chỉ nghĩ rằng nó là giá trị nói.
IFS
để chia nhỏ các lần lặp. tức là Nếu bạn lặp qua một mảng như arr=("c,3" "e,5")
và đặt IFS
trước vòng lặp for, giá trị của $i
sẽ chỉ là c
và e
, nó sẽ tách ra 3
và 5
do đó set
sẽ không phân tích cú pháp chính xác vì $i
sẽ không có bất kỳ thứ gì để phân tích cú pháp. Điều này có nghĩa là nếu các giá trị để lặp không IFS
được đặt trong dòng, thì giá trị phải được đặt bên trong vòng lặp và giá trị bên ngoài phải tôn trọng dấu phân cách dự định cho biến để lặp lại. Trong trường hợp của $tuples
nó, nó chỉ nên IFS=
là mặc định và phân chia theo khoảng trắng.
Tôi tin rằng giải pháp này gọn gàng hơn một chút so với các giải pháp khác đã được gửi, hãy xem hướng dẫn kiểu bash này để minh họa cách đọc có thể được sử dụng để chia các chuỗi ở dấu phân cách và gán chúng cho các biến riêng lẻ.
for i in c,3 e,5; do
IFS=',' read item1 item2 <<< "${i}"
echo "${item1}" and "${item2}"
done
Dựa trên câu trả lời được đưa ra bởi @ eduardo-ivanec mà không cần thiết lập / đặt lại IFS
, người ta có thể chỉ cần làm:
for i in "c 3" "e 5"
do
set -- $i
echo $1 and $2
done
Đầu ra:
c and 3
e and 5
Sử dụng mảng kết hợp (còn được gọi là từ điển / 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
Hoạt động cho bash4.0 +.
Nếu bạn cần bộ ba thay vì cặp, bạn có thể sử dụng cách tiếp cận chung hơn:
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)
, được cài đặt thông qua brew, vì vậy YMMV.
GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
Ubuntu 14.04 trong bộ chứa docker.
-A
chúng tôi có -a
.
declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
v.v. Tôi chưa thử nó trong thiết bị đầu cuối, nhưng tôi nghĩ nó sẽ hoạt động.
c=('a' 'c')
n=(3 4 )
for i in $(seq 0 $((${#c[*]}-1)))
do
echo ${c[i]} ${n[i]}
done
Đôi khi có thể tiện dụng hơn.
Để giải thích ugly
một phần, như đã lưu ý trong các bình luận:
seq 0 2 tạo ra chuỗi số 0 1 2. $ (cmd) là lệnh thay thế, vì vậy trong ví dụ này, đầu ra của seq 0 2
, là chuỗi số. Nhưng giới hạn trên là $((${#c[*]}-1))
gì?
$ ((somthing)) là một khai triển số học, vì vậy $ ((3 + 4)) là 7, v.v. Biểu thức của chúng ta là ${#c[*]}-1
, vì vậy một cái gì đó - 1. Khá đơn giản, nếu chúng ta biết nó ${#c[*]}
là gì .
c là một mảng, c [*] chỉ là toàn bộ mảng, $ {# c [*]} là kích thước của mảng trong trường hợp của chúng ta là 2. Bây giờ chúng ta quay trở lại tất cả mọi thứ: for i in $(seq 0 $((${#c[*]}-1)))
là for i in $(seq 0 $((2-1)))
là for i in $(seq 0 1)
là for i in 0 1
. Vì phần tử cuối cùng trong mảng có chỉ số là độ dài của Mảng - 1.
for i in $(seq 0 $(($#c[*]}-1))); do [...]
Sử dụng song song GNU:
parallel echo {1} and {2} ::: c e :::+ 3 5
Hoặc là:
parallel -N2 echo {1} and {2} ::: c 3 e 5
Hoặc là:
parallel --colsep , echo {1} and {2} ::: c,3 e,5
gnu parallel
brew install parallel
Sử dụng printf
thay thế quy trình:
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
Trên đây yêu cầu bash
. Nếu bash
không được sử dụng thì hãy sử dụng đường dẫn đơn giả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
ví dụ:
$ 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
Tham gia nhiều hơn một chút, nhưng có thể hữu ích:
a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
Nhưng điều gì sẽ xảy ra nếu tuple lớn hơn k / v mà một mảng kết hợp có thể chứa? Nếu đó là 3 hoặc 4 phần tử thì sao? Người ta có thể mở rộng khái niệm này:
###---------------------------------------------------
### 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
Sau đó, đầu ra sẽ giống như sau:
$ ./assoc-array-tinkering.sh
Print all elements in the array...
ya1 - ya2 - ya3 - ya4
ye1 - ye2 - ye3 - ye4
yo1 - yo2 - yo3 - yo4
Và số lượng phần tử bây giờ là không có giới hạn. Không tìm kiếm phiếu bầu; chỉ suy nghĩ thành tiếng. REF1 , REF2
Trong trường hợp các định nghĩa tuple của tôi phức tạp hơn, tôi muốn đặt chúng trong một heredoc:
while IFS=", " read -ra arr; do
echo "${arr[0]} and ${arr[1]}"
done <<EOM
c, 3
e, 5
EOM
Điều này kết hợp việc lặp lại các dòng của một heredoc với việc tách các dòng ở một số ký tự phân tách mong muốn .