วนรอบตัวแปร


16

ฉันกำลังเขียนสคริปต์ทุบตีเพื่อใช้ rsync และอัปเดตไฟล์บนเซิร์ฟเวอร์ที่แตกต่างกันประมาณ 20 แห่ง

ฉันมีส่วน rsync ที่คิดออก สิ่งที่ฉันมีปัญหาคือต้องผ่านรายการตัวแปร

สคริปต์ของฉันมีลักษณะเช่นนี้:

#!/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

[Server IP Address]ค่าของตัวแปรที่เกี่ยวข้องควรอยู่ที่ไหน ดังนั้นเมื่อ i = 1 ฉันควร echo ค่าของ $ SERVER1

ฉันลองทำซ้ำหลายครั้งรวมถึง

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

เป็นเวลานานแล้วที่ฉันเขียนสคริปต์ดังนั้นฉันจึงรู้ว่าตัวเองขาดอะไรไป รวมทั้งฉันแน่ใจว่าฉันกำลังผสมในสิ่งที่ฉันสามารถทำได้โดยใช้ C # ซึ่งฉันใช้มา 11 ปีแล้ว

เป็นสิ่งที่ฉันพยายามจะทำไปได้แม้กระทั่ง? หรือฉันควรจะใส่ค่าเหล่านี้ในอาร์เรย์และวนลูปผ่านอาร์เรย์? ฉันต้องการสิ่งเดียวกันนี้สำหรับที่อยู่ IP ที่ผลิตรวมถึงชื่อที่ตั้ง

ทั้งหมดนี้เป็นความพยายามที่จะไม่ต้องทำบล็อครหัสซ้ำฉันจะใช้เพื่อซิงค์ไฟล์บนเซิร์ฟเวอร์ระยะไกล


ขอบคุณสำหรับคำตอบและความคิดเห็นทั้งหมด ฉันจะใช้แนวทางอาร์เรย์มากกว่า
พอลสโตเนอร์

คำตอบ:


33

ใช้อาร์เรย์

#! /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; และโปรดทราบว่าคุณสามารถใส่ช่องว่างตามอำเภอใจก่อน / หลัง / ระหว่างองค์ประกอบอาร์เรย์ดังนั้นคุณสามารถ (ถ้าต้องการ) ใส่องค์ประกอบหนึ่งต่อบรรทัดเช่นเดียวกับใน OP
ruakh

@ruakh: ขอบคุณอัปเดตเพื่อแสดงสิ่งที่สามารถทำได้
choroba

28

ในขณะที่คำตอบอื่น ๆ ชี้ให้เห็นอาร์เรย์เป็นวิธีที่สะดวกที่สุดในการทำเช่นนี้ อย่างไรก็ตามเพื่อความสมบูรณ์สิ่งที่แน่นอนที่คุณจะถามหาคือการขยายตัวทางอ้อม เขียนซ้ำดังนี้ตัวอย่างของคุณจะทำงานโดยใช้วิธีนี้:

#!/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

หากคุณตกลงเพียงใส่รายการที่อยู่ IP ในการforวนซ้ำคุณอาจพิจารณาใช้การขยายรั้งบางอย่างเพื่อทำซ้ำสิ่งที่คุณต้องการ:

#!/bin/bash

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

แต่ถ้าคุณจำเป็นต้องใช้รายการ (อาจจะขยายรั้ง) นำมาใช้ใหม่แล้วการใช้รายการเพื่อเริ่มต้นอาร์เรย์จะเป็นวิธีที่จะไป:

#!/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

ในขณะที่ฉันอาจจะไปกับหนึ่งในอาร์เรย์ตอบตัวเองฉันอยากจะชี้ให้เห็นว่ามันเป็นไปได้ที่จะวนรอบชื่อโดยตรง คุณทำได้

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

หรือใน 4.3 และสูงกว่าคุณสามารถใช้nameref:

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

Hat tip ilkkachuสำหรับโซลูชัน <4.3


2
การขยายตัวทางอ้อม ( ${!var}) ใช้ได้กับ Bash เวอร์ชั่นเก่าด้วย:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu

@ilkkachu ขอบคุณสำหรับความช่วยเหลือ ฉันอัพเดตคำตอบแล้ว
kojiro

6

ทุกคนที่กล่าวว่าคุณควรใช้อาเรย์นั้นถูกต้อง แต่ - ในฐานะที่เป็นแบบฝึกหัดทางวิชาการ - ถ้าคุณตั้งใจที่จะทำมันด้วยตัวแปรแยกที่ลงท้ายด้วยตัวเลข (SERVER1, SERVER2 ฯลฯ ) นี่คือวิธีที่คุณทำ มัน:

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

evalยากที่จะใช้อย่างปลอดภัย ใช่สิ่งนี้สามารถใช้งานได้ แต่การขยายตัวทางอ้อมคือวิธีที่ดีกว่าโดยทั่วไป
Peter Cordes

นิสัย. เมื่อฉันเริ่มเขียนเชลล์สคริปต์แม้ใน bash ไม่มีการขยายตัวทางอ้อม "eval" ปลอดภัยอย่างสมบูรณ์ถ้าคุณเข้าใจวิธีการหลบหนีอย่างถูกต้อง ถึงกระนั้นฉันเห็นด้วยกับคุณว่าการขยายตัวทางอ้อมนั้นดีกว่า ... แต่วิธีการเดิมยังคงใช้ได้ ฉันจะไม่ทำเช่นนั้นในกรณีนี้ ฉันจะใช้อาร์เรย์!
Beam Davis

ในกรณีนี้จะแจ้งให้ทราบว่าคุณไม่ได้หลบหนีราคาคู่ดังนั้นหลังจากevalเสร็จแล้วคุณมีไม่echo $SERVER1 echo "$SERVER1"ใช่เป็นไปได้ที่จะใช้อย่างปลอดภัย แต่ใช้งานง่ายมากอย่างไม่ปลอดภัยหรือไม่ว่าคุณตั้งใจ!
Peter Cordes

เนื่องจากชื่อเซิร์ฟเวอร์จะไม่มีช่องว่างจึงไม่มีความสำคัญในกรณีนี้ต่อไป ... แต่คุณถูกต้องควรใช้เครื่องหมายอัญประกาศคู่แทน แก้ไขคำตอบ!
Beam Davis

ใช่การลบเครื่องหมายคำพูดคู่ทั้งหมด (เพื่อหลีกเลี่ยงการแสดงผลที่ทำให้เข้าใจผิดว่าพวกเขาปกป้องสิ่งใด) จะเป็นตัวเลือกอื่น แต่การหลบหนีพวกเขาก็เป็นวิธีที่ธรรมดากว่าดังนั้น +1
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.