Variabel Loop Through


16

Saya menulis skrip bash untuk menggunakan rsync dan memperbarui file di sekitar 20 server yang berbeda.

Saya memiliki bagian rsync yang tahu. Apa yang saya mengalami masalah adalah melalui daftar variabel.

Skrip saya sejauh ini terlihat seperti ini:

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

Di mana [Server IP Address]seharusnya nilai dari variabel terkait. Jadi ketika saya = 1 saya harus mengulangi nilai $ SERVER1.

Saya sudah mencoba beberapa iterasi termasuk ini

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

Sudah lama sejak saya menulis sehingga saya tahu saya kehilangan sesuatu. Plus, saya yakin saya mencampur apa yang bisa saya lakukan menggunakan C #, yang telah saya gunakan selama 11 tahun terakhir.

Apakah yang saya coba lakukan bahkan mungkin? Atau haruskah saya meletakkan nilai-nilai ini dalam array dan perulangan melalui array? Saya perlu hal yang sama untuk alamat IP produksi serta nama lokasi.

Ini semua dalam upaya untuk tidak mengulangi blok kode yang akan saya gunakan untuk menyinkronkan file di server jauh.


Terima kasih atas semua tanggapan dan komentar. Saya kemungkinan besar akan mengambil pendekatan array.
Paul Stoner

Jawaban:


33

Gunakan array.

#! /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; dan perhatikan bahwa Anda dapat meletakkan spasi kosong sebelum / sesudah / di antara elemen array, sehingga Anda dapat (jika diinginkan) meletakkan satu elemen per baris seperti pada OP.
ruakh

@ruakh: terima kasih, diperbarui untuk menunjukkan apa yang bisa dilakukan.
choroba

28

Seperti jawaban lain tunjukkan, array adalah cara yang paling nyaman untuk melakukan ini. Namun, untuk kelengkapan, hal yang persis Anda minta adalah ekspansi tidak langsung . Ditulis ulang sebagai berikut, sampel Anda juga akan berfungsi menggunakan metode ini:

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

Jika Anda setuju dengan hanya meletakkan daftar alamat IP dalam forloop, maka Anda mungkin juga mempertimbangkan untuk menggunakan beberapa ekspansi brace untuk beralih pada apa pun yang Anda butuhkan:

#!/bin/bash

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

Tetapi jika Anda perlu menggunakan kembali daftar (mungkin diperluas), kemudian menggunakan daftar untuk menginisialisasi array akan menjadi cara untuk pergi:

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

Sementara saya mungkin akan pergi dengan salah satu jawaban array sendiri, saya ingin menunjukkan bahwa adalah mungkin untuk mengulang nama secara langsung. Anda dapat melakukan

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

atau, dalam 4.3 dan lebih tinggi, Anda dapat menggunakan nameref:

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

Hat hat ilkkachu untuk solusi <4.3.


2
Ekspansi tidak langsung ( ${!var}) juga berfungsi dengan versi Bash yang lebih lama:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu

@ilkkachu terima kasih atas bantuannya. Saya telah memperbarui jawaban saya.
kojiro

6

Setiap orang yang mengatakan Anda harus menggunakan array benar, tetapi - sebagai latihan akademis - jika Anda bertekad untuk melakukannya dengan variabel terpisah yang diakhiri dengan angka (SERVER1, SERVER2, dll.), Ini adalah bagaimana Anda akan melakukannya Itu:

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

evalsulit digunakan dengan aman. Ya ini bisa bekerja, tetapi bash ekspansi tidak langsung adalah cara yang lebih baik secara umum.
Peter Cordes

Kebiasaan. Ketika saya mulai menulis skrip shell, bahkan dalam bash, tidak ada ekspansi tidak langsung. "eval" sangat aman jika Anda mengerti cara melarikan diri dengan benar. Tetap saja, saya setuju dengan Anda bahwa ekspansi tidak langsung lebih baik ... tetapi cara lama masih bekerja. Saya juga tidak akan melakukan hal ini. Saya akan menggunakan array!
Beam Davis

Dalam hal ini, perhatikan bahwa Anda tidak lolos dari tanda kutip ganda, jadi setelah evalselesai, Anda harus echo $SERVER1, tidak echo "$SERVER1". Ya mungkin untuk menggunakan dengan aman, tetapi sangat mudah digunakan dengan tidak aman atau tidak seperti yang Anda inginkan!
Peter Cordes

Karena nama server tidak mengandung spasi, toh tidak akan menjadi masalah dalam kasus ini ... tetapi Anda benar, tanda kutip ganda harus diloloskan. Diperbaiki dalam menjawab!
Beam Davis

Benar, menghapus tanda kutip ganda sepenuhnya (untuk menghindari kesan menyesatkan bahwa mereka melindungi apa pun) akan menjadi pilihan lain. Tetapi melarikan diri juga merupakan cara yang lebih umum, jadi +1.
Peter Cordes
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.