1

Overview: I save my variable in a config file and call them later.

Each entry with the name FailOverVM has a number beside it like FailOverVM1 and I want to check to see if it has data and generate a function named FailOverVM1() that later in the script starts $FailOverVM1Name, which happens to be 'Plex Server'

I can manually do it like StartVM1() and i works but I may expand to 15 later and want it to adjust accordingly.

To clarify I can start the VM with a Case statement later and have but I can't wrap my head around the variable that in itself is a variable. I hope I didn't confuse anyone. Maybe im making this WAY more complicated than it is or needs to be.

#!/bin/bash
. "${BASH_SOURCE%/*}/configlocation.conf"
. $Configuration
checkVM1=$(nc -vvz $FailOverVM1IP $FailOverVM1Port 2>&1)
VMCount=$(grep "FailOverVM.Name" /media/VirtualMachines/Current/Configuration.conf | wc -l)
pinggateway=$(ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null && echo ok || echo error = error)
STATE="error";

while [  $STATE == "error" ]; do
    #do a ping and check that its not a default message or change to grep for something else
    STATE=$(ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null && echo ok || echo error)

    #sleep for 2 seconds and try again
    sleep 2
 done

for i $VMCount; do
if [ -z "$FailOverVM$VMCountName" ];
    echo "$FailOverVM$VMCountName"
fi
done

StartVM1(){
  if [[ $checkVM1 = "Connection to $FailOverVM1IP $FailOverVM1Port port [tcp/*] succeeded!" ]]; then
    echo '$FailOverVM1Name is up'
  else
   echo "$FailOverVM1Name down"
   su -c 'VBoxManage startvm $FailOverVM1Name -type headless' vbox
  fi
}

Where I'v gotten so far in a test script

#!/bin/bash
. "${BASH_SOURCE%/*}/configlocation.conf"
. $Configuration
Pre='$FailOverVM'
post="FailOverVM"
name="Name"
VMCount=$(grep "FailOverVM.Name" $Configuration | wc -l) #Count entires in config file wirn FailOverVM*Name
while [[ $i -le $VMCount ]]
do
#if [ -z $Pre$i"Name" ];then #If the variable $FailOverVM*Name is not blank
   $post$i=$Pre$i$Name
   echo "$post$i" #print it
#else
#    echo $Pre$i"Name" "was empty"
#fi
    ((i = i + 1))
done

Output:

./net2.sh: line 11: FailOverVM=$FailOverVM: command not found
FailOverVM
./net2.sh: line 11: FailOverVM1=$FailOverVM1: command not found
FailOverVM1
./net2.sh: line 11: FailOverVM2=$FailOverVM2: command not found
FailOverVM2
./net2.sh: line 11: FailOverVM3=$FailOverVM3: command not found
FailOverVM3
./net2.sh: line 11: FailOverVM4=$FailOverVM4: command not found
FailOverVM4
./net2.sh: line 11: FailOverVM5=$FailOverVM5: command not found
FailOverVM5
./net2.sh: line 11: FailOverVM6=$FailOverVM6: command not found
FailOverVM6

The problem here is there is no $FailOverVM without a number beside it, and what is up with "command not found FailOverVM5" (or any other number) I didn't know I issued one. But the biggest problem is its not grabbing the variable $FailOVerVM* form the config file. I need that for the func loop.




New modified script with @dave_thompson_085 help

#!/bin/bash
. "${BASH_SOURCE%/*}/configlocation.conf"
. $Configuration
for i in ${!FailOverName[@]}; do 
  selip=FailOverIP[${i}]
  selport=FailOverPort[${i}]
  checkVM[$i]=$(nc -vvz ${!selip} ${!selport} 2>/devnull)
  echo ${!selip}
  echo ${!selport}
  echo FailOverName[${i}]
done

StartVM() { # first argument to a function is accessed as $1 or ${1}
  selname=FailOverName[${i}]
  if [[ checkVM[$i] =~ 'succeeded' ]]; then # only need to check the part that matters
    echo number $i name ${!selname} already up 
  else 
    echo starting number $i name ${!selname}
    echo su -c "VboxManager startvm '${!selname}' -headless" vbox # note " because ' $
  fi
}
#done
StartVM 1 # and 
StartVM 2 # etc

Output

root@6120:~/.scripts# ./net2.sh -v
192.168.1.6
32400
FailOverName[1]
192.168.1.5
80
FailOverName[2]
192.168.1.7
80
FailOverName[3]
192.168.1.1
1030
FailOverName[4]
starting number 4 name finch
su -c VboxManager startvm 'finch' -headless vbox
starting number 4 name finch
su -c VboxManager startvm 'finch' -headless vbox
root@6120:~/.scripts# 

Config file

#
FailOverVM1IP='192.168.1.6'
FailOverVM1Port='32400'
FailOverVM1Name='Plex Server'
FailOverVM1NASHDD='/media/VirtualMachines/Current/Plex\ Server/Plex\ Server.vmdk'
FailOverVM1LocalHDD='/home/vbox/VirtualBox\ VMs/Plex\ Server/Plex\ Server.vmdk'

FailOverVM2IP='192.168.1.7'
FailOverVM2Port='32402'
FailOverVM1Name='Plex Server2'
FailOverVM2NASHDD='/media/VirtualMachines/Current/Plex\ Server/Plex\ Server.vmdk'
FailOverVM2LocalHDD='/home/vbox/VirtualBox\ VMs/Plex\ Server/Plex\ Server.vmdk'

FailOverVM3IP='192.168.1.8'
FailOverVM3Port='32403'
FailOverVM3Name='Plex Server3'
FailOverVM3NASHDD='/media/VirtualMachines/Current/Plex\ Server/Plex\ Server.vmdk'
FailOverVM3LocalHDD='/home/vbox/VirtualBox\ VMs/Plex\ Server/Plex\ Server.vmdk'

FailOverVM4IP='192.168.1.9'
FailOverVM4Port='32404'
FailOverVM4Name='Plex Server4'
FailOverVM4NASHDD='/media/VirtualMachines/Current/Plex\ Server/Plex\ Server.vmdk'
FailOverVM4LocalHDD='/home/vbox/VirtualBox\ VMs/Plex\ Server/Plex\ Server.vmdk'

FailOverVM5IP='192.168.1.10'
FailOverVM5Port='32405'
FailOverVM5Name='Plex Server5'
FailOverVM5NASHDD='/media/VirtualMachines/Current/Plex\ Server/Plex\ Server.vmdk'
FailOverVM5LocalHDD='/home/vbox/VirtualBox\ VMs/Plex\ Server/Plex\ Server.vmdk'

FailOverIP[1]=192.168.1.6 FailOverName[1]=robin FailOverPort[1]=32400
FailOverIP[2]=192.168.1.5 FailOverName[2]=bluejay FailOverPort[2]=80
FailOverIP[3]=192.168.1.7 FailOverName[3]=sparrow FailOverPort[3]=80
FailOverIP[4]=192.168.1.1 FailOverName[4]=finch FailOverPort[4]=1030

VM1LogDirLogDir='/media/VirtualMachines/Logs/Plextstart'


PlexServerIP='192.168.1.6'
PlexPort='32400'
mydate=`date '+%Y-%m-%d_%H%M'`
rsyncfrom=
NASPlexvmHDD='/media/VirtualMachines/Current/Plex\ Server/Plex\ Server.vmdk'
LocalPlexvmDHDD='/home/vbox/VirtualBox\ VMs/Plex\ Server/Plex\ Server.vmdk'


PlexVMname='Plex Server'
PlexStartLogDir='/media/VirtualMachines/Logs/Plextstart'
RouterIp='192.168.1.1'

So it sees all the vms but is only executing the last and twice at that.



#!/bin/bash
. "${BASH_SOURCE%/*}/configlocation.conf"
. $Configuration
for i in ${!FailOverName[@]}; do 
  selip=FailOverIP[${i}]
  selport=FailOverPort[${i}]
  checkVM[$i]=$(nc -vvz ${!selip} ${!selport} 2>&1)
  echo ${!selip}
  echo ${!selport}
  #echo ${i}
#done

StartVM() { # first argument to a function is accessed as $1 or ${1}
  selname=FailOverName[${i}]
  if [[ $checkVM[$i] =~ 'succeeded' ]]; then # only need to check the part that matters
    echo number $i name ${!selname} already up 
  else 
    echo starting number $i name ${!selname}
    echo su -c "VboxManager startvm '${!selname}' -headless" vbox # note " because ' prevents the variable expansion
  fi
}
StartVM
done

Note: Checking of if VM is already running doesn't function yet but that wasnt the question I asked so this meets the criteria.

  • 1
    You're looking for eval – grochmal Jul 27 '16 at 4:34
  • Inside the loop i put: eval vm=$Pre$i$Name echo $vm I get blank outputs I then replaced it with eval vm=$Post$i$Name And get 1 2 3 4 etc for each instance (Only numbers no names) – sarethan Jul 27 '16 at 8:45
  • It looks to me like a very long question which in the end boils down to asking how to assign a value to a variable whose name is constructed dynamically. – Stéphane Chazelas Jul 28 '16 at 9:31
2

Asides: you can eliminate the wc -l by using grep -c FailoverVM.Name configfile.
But if you want to use numbers over 9 decimal (not e.g. 123456789abcdef) your pattern needs to be FailoverVM[0-9][0-9]?Name or FailoverVM[0-9]{1,2}Name in -E extended mode.
Also for i $VMCount is a syntax error; I assume you mean for i in $(seq $VMCount).


You can read a variable indirectly in bash with ! (bang) and another variable containing the name:

for i in $(seq $VMCount); do
  selname=FailoverVM${i}Name
  selip=FailoverVM${i}IP
  selport=FailoverVM${i}Port
  echo name ${!selname} is IP ${!selip} and port ${!selport}
done

which is less of a blunderbuss than eval but still clumsy. But you cannot set a variable this way, so you should use an array for that. And you cannot do this for functions, so instead write one function that accepts an argument to tell it which (set of) variables to use:

for i in $(seq $VMCount); do 
  selip-Failover${i}IP
  selport=Failover${i}Port
  checkVM[$i]=$(nc -vvz ${!selip} ${!selport} 2>/devnull)
}
StartVM() { # first argument to a function is accessed as $1 or ${1}
  selname=FailoverVM${1}Name
  if [[ checkVM[$1] =~ 'succeeded' ]]
  # only need to check the part that matters
  then echo number $1 name ${!selname} already up 
  else echo starting number $1
    su -c "VboxManager startvm ${!selname} -headless" vbox
  # note " because ' prevents the variable expansion
  fi
]
...
StartVM 1 # and 
StartVM 2 # etc

OTOH if you can change the config to use array variables for everything like this

FailoverIP[1]=10.255.1.1 FailoverName[1]=robin
FailoverIP[2]=10.255.2.2 FailoverName[2]=bluejay
etc

that would make everything much simpler. And then you don't need to re-grep the file to count the entries, you can just use e.g. ${#FailoverName[@]}

  • changed VMCount=$(grep "FailOverVM.Name" $Configuration | wc -l) to VMCount=$(grep -E "FailOverVM[0-9]{1,2}Name" $Configuration) changed selip-Failover${i}IP to selip=FailOver${i}IP remove } from for loop as is was casing an error. Changed all instances of Failover to FailOver Placed FailoverIP[1]=10.255.1.1 FailoverName[1]=robin and FailoverIP[2]=10.255.2.2 FailoverName[2]=bluejay in config but cant seem to reffrence them correclty. Output line 22: syntax error: unexpected end of file – sarethan Jul 27 '16 at 17:07
  • I would like to use the array in the config file, it does seem much cleaner. – sarethan Jul 27 '16 at 17:09
  • ok the for loop was missing done Is there a way i can post the changed script on a comment here with messing up the format? I made a few more adjustment and made progress thanks to your solution but needs a little more tweaking. – sarethan Jul 27 '16 at 17:49
  • I checked this the correct answer because it directly guided me to the answer of my question. There were several corrections I still had to make but had it not been for this I would not have found them. I will post in my original at the bottom the working copy. – sarethan Jul 28 '16 at 17:19
  • @sarethan the problem with the updated versions in your Q is that in StartVM you used $i ${i} (letter 'eye') which is the (unchanging) leftover value 4 from the for loop; you should use $1 ${1} (digit one) which is the argument in the particular call. – dave_thompson_085 Jul 31 '16 at 6:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.