Vòng lặp thông qua các biến


16

Tôi đang viết một tập lệnh bash để sử dụng rsync và cập nhật các tệp trên khoảng 20 máy chủ khác nhau.

Tôi có phần rsync đã tìm ra. Những gì tôi gặp rắc rối là thông qua một danh sách các biến.

Kịch bản của tôi cho đến nay trông như thế này:

#!/bin/bash
SERVER1="192.xxx.xxx.2"
SERVER2="192.xxx.xxx.3"
SERVER3="192.xxx.xxx.4"
SERVER4="192.xxx.xxx.5"
SERVER5="192.xxx.xxx.6"
SERVER6="192.xxx.xxx.7"

for ((i=1; i<7; i++))
do
    echo [Server IP Address]
done

Trường hợp [Server IP Address]nên là giá trị của biến liên quan. Vì vậy, khi i = 1 tôi nên lặp lại giá trị của $ SERVER1.

Tôi đã thử một vài lần lặp lại điều này bao gồm

echo "$SERVER$i"    # printed the value of i
echo "SERVER$i"     # printer "SERVER" plus the value of i ex: SERVER 1 where i = 1
echo $("SERVER$i")  # produced an error SERVER1: command not found where i = 1
echo $$SERVER$i     # printed a four digit number followed by "SERVER" plus the value of i
echo \$$SERVER$i    # printed "$" plus the value of i

Đã lâu lắm rồi tôi mới viết kịch bản nên tôi biết mình đang thiếu thứ gì đó. Thêm vào đó, tôi chắc chắn rằng tôi đang pha trộn những gì tôi có thể làm bằng C #, thứ mà tôi đã sử dụng trong 11 năm qua.

Là những gì tôi đang cố gắng làm thậm chí có thể? Hoặc tôi nên đặt các giá trị này trong một mảng và lặp qua mảng? Tôi cần điều tương tự cho các địa chỉ IP sản xuất cũng như tên địa điểm.

Đây là tất cả trong một nỗ lực để không phải lặp lại một khối mã tôi sẽ sử dụng để đồng bộ hóa các tệp trên máy chủ từ xa.


Cảm ơn tất cả các phản hồi và ý kiến. Tôi nhiều khả năng sẽ có cách tiếp cận mảng.
Paul Stoner

Câu trả lời:


33

Sử dụng một mảng.

#! /bin/bash
servers=( 192.xxx.xxx.2 192.xxx.xxx.3
          192.xxx.xxx.4 192.xxx.xxx.5
          192.xxx.xxx.6 192.xxx.xxx.7
)

for server in "${servers[@]}" ; do
    echo "$server"
done

6
+1; và lưu ý rằng bạn có thể đặt khoảng trắng tùy ý trước / sau / giữa các phần tử mảng, do đó bạn có thể (nếu muốn) đặt một phần tử trên mỗi dòng như trong OP.
ruakh

@ruakh: cảm ơn, cập nhật để hiển thị những gì có thể được thực hiện.
choroba

28

Như các câu trả lời khác chỉ ra, một mảng là cách thuận tiện nhất để làm điều này. Tuy nhiên, để hoàn thiện, điều chính xác mà bạn đang yêu cầu là mở rộng gián tiếp . Viết lại như sau, mẫu của bạn cũng sẽ hoạt động bằng phương pháp này:

#!/bin/bash
SERVER1="192.xxx.xxx.2"
SERVER2="192.xxx.xxx.3"
SERVER3="192.xxx.xxx.4"
SERVER4="192.xxx.xxx.5"
SERVER5="192.xxx.xxx.6"
SERVER6="192.xxx.xxx.7"

for ((i=1; i<7; i++))
do
    servervar="SERVER$i"
    echo "${!servervar}"
done

Nếu bạn ổn khi chỉ cần đưa danh sách địa chỉ IP vào forvòng lặp, thì bạn cũng có thể xem xét đơn giản là sử dụng một số mở rộng dấu ngoặc để lặp lại bất cứ điều gì bạn cần:

#!/bin/bash

for server in \
192.xxx.xxx.{2..7} \
192.yyy.yyy.{42..50} \
192.zzz.zzz.254
do
    echo "$server"
done

Nhưng nếu bạn cần sử dụng lại danh sách (có thể mở rộng bằng dấu ngoặc), thì việc sử dụng danh sách để khởi tạo một mảng sẽ là cách tốt nhất:

#!/bin/bash

servers=(
192.xxx.xxx.{2..7} 
192.yyy.yyy.{42..50}
192.zzz.zzz.254 )

for server in "${servers[@]}"
do
    echo "$server"
done

14

Mặc dù tôi có thể tự đi với một trong các câu trả lời mảng, tôi muốn chỉ ra rằng có thể lặp lại trực tiếp tên. Bạn có thể làm

for name in "${!SERVER*}"; do
    echo "${!name}"
done

hoặc, trong 4.3 trở lên, bạn có thể sử dụng nameref:

declare -n name
for name in "${!SERVER*}"; do
    echo "$name"
done

Mũ đầu ilkkachu cho giải pháp <4.3.


2
Mở rộng gián tiếp ( ${!var}) cũng hoạt động với các phiên bản cũ hơn của Bash:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu

@ilkkachu cảm ơn sự giúp đỡ. Tôi đã cập nhật câu trả lời của mình.
kojiro

6

Mọi người nói bạn nên sử dụng một mảng là đúng, nhưng - như một bài tập học thuật - nếu bạn quyết tâm thực hiện nó với các biến riêng biệt kết thúc bằng một số (SERVER1, SERVER2, v.v.), đây là cách bạn làm nó:

for ((i=1; i<7; i++))
do
    eval echo \"\$SERVER$i\"
done

evalkhó sử dụng một cách an toàn. Vâng, điều này có thể làm việc, nhưng bash mở rộng gián tiếp là một cách tốt hơn nói chung.
Peter Cordes

Thói quen. Khi tôi bắt đầu viết shell script, ngay cả trong bash, không có sự mở rộng gián tiếp. "Eval" hoàn toàn an toàn nếu bạn hiểu cách trốn thoát đúng cách. Tuy nhiên, tôi đồng ý với bạn rằng mở rộng gián tiếp là tốt hơn ... nhưng những cách cũ vẫn hoạt động. Tôi sẽ không làm gì trong trường hợp này. Tôi sẽ sử dụng một mảng!
Beam Davis

Trong trường hợp này, lưu ý rằng bạn đã không thoát khỏi dấu ngoặc kép, vì vậy sau khi evalhoàn thành, bạn có echo $SERVER1, không echo "$SERVER1". Có thể sử dụng một cách an toàn, nhưng rất dễ sử dụng một cách không an toàn hoặc không như bạn dự định!
Peter Cordes

Vì tên máy chủ sẽ không chứa dấu cách, nên dù sao thì nó cũng không thành vấn đề ... nhưng bạn đã đúng, dấu ngoặc kép sẽ được thoát. Đã sửa trong câu trả lời!
Beam Davis

Phải, loại bỏ hoàn toàn dấu ngoặc kép (để tránh ấn tượng sai lệch rằng họ đang bảo vệ bất cứ điều gì) sẽ là lựa chọn khác. Nhưng thoát khỏi chúng cũng là cách chung chung hơn, nên +1.
Peter Cordes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.