Петля через змінні


16

Я пишу bash-сценарій для використання 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]має бути значення пов'язаної змінної. Тож, коли я = 1, я повинен відповідати значенням $ 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; і зауважте, що ви можете розмістити довільну пробіл до / після / між елементами масиву, так що ви можете (за бажанням) поставити один елемент на рядок, як в ОП.
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

Наконечник капелюха ilkkachu для розчину <4.3.


2
Непряме розширення ( ${!var}) працює і зі старими версіями Bash:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu

@ilkkachu дякую за допомогу. Я оновив свою відповідь.
якийро

6

Кожен, хто сказав, що ви повинні використовувати масив, є правильним, але - як академічна вправа - якби ви вирішили це робити з окремими змінними, що закінчуються на число (SERVER1, SERVER2 тощо), саме так ви зробите. це:

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

evalважко використовувати безпечно. Так, це може спрацювати, але загалом непряме розширення є кращим способом.
Пітер Кордес

Звичка. Коли я почав писати сценарії оболонок, навіть у баші, не було непрямого розширення. "eval" цілком безпечний, якщо ви розумієте, як правильно врятуватися. І все-таки я згоден з вами, що непряме розширення краще ... але старі способи все ще працюють. Я б у цьому випадку не робив жодного. Я б використав масив!
Промінь Девіса

У цьому випадку зауважте, що ви не уникнули подвійних лапок, тож після того, як evalбуде зроблено, у вас є echo $SERVER1, ні echo "$SERVER1". Так можна використовувати безпечно, але дуже просто використовувати небезпечно чи не так, як ви задумали!
Пітер Кордес

Оскільки ім’я сервера не міститиме пробілів, воно б у будь-якому разі не мало значення ... але ви праві, подвійні лапки слід уникати. Виправлено у відповідь!
Промінь Девіса

Правильно, видалення подвійних лапок повністю (щоб уникнути оманливого враження, що вони щось захищають) було б іншим варіантом. Але втеча від них теж є більш загальним способом, тому +1.
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.