Passa attraverso le variabili


16

Sto scrivendo uno script bash per utilizzare rsync e aggiornare i file su circa 20 server diversi.

Ho capito la parte rsync. Ciò di cui ho problemi è passare attraverso un elenco di variabili.

La mia sceneggiatura finora assomiglia a questa:

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

Dove [Server IP Address]dovrebbe essere il valore della variabile associata. Quindi quando i = 1 dovrei fare eco al valore di $ SERVER1.

Ho provato diverse iterazioni di questo tra cui

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

È da tanto tempo che scrivo, quindi so che mi manca qualcosa. Inoltre sono sicuro che sto mescolando ciò che potrei fare usando C #, che ho usato negli ultimi 11 anni.

Ciò che sto cercando di fare è anche possibile? O dovrei mettere questi valori in un array e passare in rassegna l'array? Ho bisogno della stessa cosa per gli indirizzi IP di produzione e per i nomi delle località.

Tutto questo nel tentativo di non dover ripetere un blocco di codice che userò per sincronizzare i file sul server remoto.


Grazie per tutte le risposte e i commenti. Molto probabilmente prenderò l'approccio array.
Paul Stoner,

Risposte:


33

Usa un 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; e nota che puoi inserire spazi bianchi arbitrari prima / dopo / tra gli elementi dell'array, così puoi (se lo desideri) inserire un elemento per riga come nell'OP.
Ruakh,

@ruakh: grazie, aggiornato per mostrare cosa si può fare.
Choroba,

28

Come sottolineato dalle altre risposte, un array è il modo più conveniente per farlo. Tuttavia, per completezza, la cosa esatta che stai chiedendo è l'espansione indiretta . Riscritto come segue, anche il tuo esempio funzionerà usando questo metodo:

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

Se stai bene semplicemente inserendo l'elenco degli indirizzi IP nel forloop, potresti anche considerare semplicemente l'uso di alcune espansioni di parentesi graffe per iterare su tutto ciò di cui hai bisogno:

#!/bin/bash

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

Ma se hai bisogno di riutilizzare l'elenco (possibilmente espanso), utilizzare la lista per inizializzare un array sarebbe la strada da percorrere:

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

Anche se probabilmente andrei con una delle risposte dell'array, vorrei sottolineare che è possibile eseguire il loop over dei nomi direttamente. Tu puoi fare

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

oppure, in 4.3 e versioni successive, è possibile utilizzare un nameref:

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

Punta del cappello ilkkachu per la soluzione <4.3.


2
L'espansione indiretta ( ${!var}) funziona anche con le versioni precedenti di Bash:foo1=aa; foo2=bbb; foox=cccc; for p in "${!foo@}"; do echo "$p = ${!p}"; done
ilkkachu,

@ilkkachu grazie per l'assistenza. Ho aggiornato la mia risposta.
Kojiro,

6

Tutti quelli che hanno detto che dovresti usare un array sono corretti, ma - come esercizio accademico - se fossi determinato a farlo con variabili separate che finiscono in un numero (SERVER1, SERVER2, ecc.), Ecco come faresti esso:

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

evalè difficile da usare in sicurezza. Sì, questo può funzionare, ma bash l'espansione indiretta è un modo migliore in generale.
Peter Cordes,

Abitudine. Quando ho iniziato a scrivere script di shell, anche in bash, non c'è stata un'espansione indiretta. "eval" è perfettamente sicuro se si capisce come fuggire correttamente. Tuttavia, sono d'accordo con te sul fatto che l'espansione indiretta è migliore ... ma i vecchi metodi funzionano ancora. Non farei neanche in questo caso. Userei un array!
Beam Davis,

In questo caso, notare che non hai sfuggire le virgolette, così dopo evalè fatto, avete echo $SERVER1, non è echo "$SERVER1". Sì, è possibile utilizzarlo in modo sicuro, ma è molto facile da usare in modo non sicuro o no come previsto!
Peter Cordes,

Dato che il nome di un server non contiene spazi, in questo caso non avrebbe avuto importanza in questo caso ... ma hai ragione, le doppie virgolette dovrebbero essere sfuggite. Risolto in risposta!
Beam Davis,

Bene, rimuovere completamente le doppie virgolette (per evitare l'impressione fuorviante di proteggere qualcosa) sarebbe l'altra opzione. Ma fuggire anche da loro è il modo più generico, quindi +1.
Peter Cordes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.