Building Dynamic Variable Names in KornShell - shell

I did a search but did not find anything quite like what I am trying to do.
I have a list of Server Hostnames & IPs
Servera | IPa
Serverb | IPb
Servern | IPn
I want to cat this file and put each element into variables
Server_Var_1
IP_Var_1
Server_Var_2
IP_Var_2
Server_Var_n
IP_Var_n
What I currently have is the following KornShell (ksh):
Counter=0
cat hostfile|while read line; do
Server_Var_"$Counter"=echo $line | awk -F"|" '{print $1}'
IP_Var_"$Counter"=echo $line | awk -F"|" '{print $2}'
echo $Server_Var_[*] $IP_Var_[*]
done
Any help is appreciated.

$ cat hostfile
server1 | 192.168.1.101
server2 | 192.168.1.102
server3 | 192.168.1.103
$ cat foo
#!/bin/sh
counter=0
while IFS=" |" read name ip; do
eval Server_VAR_$counter=$name
eval IP_VAR_$counter=$ip
: $(( counter += 1 ))
done < hostfile
echo $Server_VAR_0:$IP_VAR_0
echo $Server_VAR_1:$IP_VAR_1
echo $Server_VAR_2:$IP_VAR_2
$ ./foo
server1:192.168.1.101
server2:192.168.1.102
server3:192.168.1.103

Here's a slight twist to the original question (which was perfectly answered by #William Pursell). So this bit of code will produce the same output, but uses an array of compound variables instead. Note that this is specific to ksh93.
$ cat read_hostvars
#!/bin/sh
counter=0
typeset -a Server
while IFS=" |" read name ip; do
Server[counter].name=$name
Server[counter].ip=$ip
(( counter++ ))
done < hostfile
for n in ${!Server[#]}; do
echo ${Server[n].name}:${Server[n].ip}
done

Related

Bash script to read from a file and save the information in an array?

I want to read from a file that has host IPs written in it and save it in an array. So far I have tried this:
Host=`cat /home/hp3385/Desktop/config | egrep '^Host' | awk '{print $2}'`
But I don't think that it saves the information in an array. What is the type of the variable 'Host'? If it's not an array how can I convert it into one?
This is a sample data from the file /home/hp3385/Desktop/config:
############# Server1 #################
Host 8.8.8.8
Hostname google
############# Server2 ################
Host 8.8.4.4
Hostname google
The expected output is:
a=($'8.8.8.8' $'8.8.4.4')
You can try this
myarray=()
while read -r line; do
if echo "$line" | grep -q 'Host '; then
myarray+=($(echo "$line" | awk '/^Host/ {print $2}'))
fi
done < /home/hp3385/Desktop/config
Declaring an array:
ARRAY=(0 1 2 3 4 5)
So your array can be declared like this:
HOSTS=($(awk '/^Host/ {print $2}' YOUR_FILE))
If you want to know the amount of values in your array:
echo ${#HOSTS[*]}
To get an output of all values in your array (credit goes to triplee):
printf '%s\n' "${HOSTS[#]}"

Splitting a text in Unix

I am writing a simple script that splits a variable that holds some text by using below code:
#!/bin/sh
SAMPLE_TEXT=hello.world.testing
echo $SAMPLE_TEXT
OUT_VALUE=$SAMPLE_TEXT | cut -d'.' -f1
echo output is $OUT_VALUE
I am expecting output as output is hello but when I run this program then I am getting output as output is. Please let me know where I am doing mistake?
To evaluate a command and store it into a variable, use var=$(command).
All together, your code works like this:
SAMPLE_TEXT="hello.world.testing"
echo "$SAMPLE_TEXT"
OUT_VALUE=$(echo "$SAMPLE_TEXT" | cut -d'.' -f1)
# OUT_VALUE=$(cut -d'.' -f1 <<< "$SAMPLE_TEXT") <--- alternatively
echo "output is $OUT_VALUE"
Also, note I am adding quotes all around. It is a good practice that will help you in general.
Other approaches:
$ sed -r 's/([^\.]*).*/\1/g' <<< "$SAMPLE_TEXT"
hello
$ awk -F. '{print $1}' <<< "$SAMPLE_TEXT"
hello
$ echo "${SAMPLE_TEXT%%.*}"
hello
The answer by fedorqui is the correct answer. Just adding another approach...
$ SAMPLE_TEXT=hello.world.testing
$ IFS=. read OUT_VALUE _ <<< "$SAMPLE_TEXT"
$ echo output is $OUT_VALUE
output is hello
Just to expand on #anishane's comment to his own answer:
$ SAMPLE_TEXT="hello world.this is.a test string"
$ IFS=. read -ra words <<< "$SAMPLE_TEXT"
$ printf "%s\n" "${words[#]}"
hello world
this is
a test string
$ for idx in "${!words[#]}"; do printf "%d\t%s\n" $idx "${words[idx]}"; done
0 hello world
1 this is
2 a test string

Variable loss in redirected bash while loop

I have the following code
for ip in $(ifconfig | awk -F ":" '/inet addr/{split($2,a," ");print a[1]}')
do
bytesin=0; bytesout=0;
while read line
do
if [[ $(echo ${line} | awk '{print $1}') == ${ip} ]]
then
increment=$(echo ${line} | awk '{print $4}')
bytesout=$((${bytesout} + ${increment}))
else
increment=$(echo ${line} | awk '{print $4}')
bytesin=$((${bytesin} + ${increment}))
fi
done < <(pmacct -s | grep ${ip})
echo "${ip} ${bytesin} ${bytesout}" >> /tmp/bwacct.txt
done
Which I would like to print the incremented values to bwacct.txt, but instead the file is full of zeroes:
91.227.223.66 0 0
91.227.221.126 0 0
127.0.0.1 0 0
My understanding of Bash is that a redirected for loop should preserve variables. What am I doing wrong?
First of all, simplify your script! Usually there are many better ways in bash. Also most of the time you can rely on pure bash solutions instead of running awk or other tools.
Then add some debbuging!
Here is a bit refactored script with debugging
#!/bin/bash
for ip in "$(ifconfig | grep -oP 'inet addr:\K[0-9.]+')"
do
bytesin=0
bytesout=0
while read -r line
do
read -r subIp _ _ increment _ <<< "$line"
if [[ $subIp == "$ip" ]]
then
((bytesout+=increment))
else
((bytesin+=increment))
fi
# some debugging
echo "line: $line"
echo "subIp: $subIp"
echo "bytesin: $bytesin"
echo "bytesout: $bytesout"
done <<< "$(pmacct -s | grep "$ip")"
echo "$ip $bytesin $bytesout" >> /tmp/bwacct.txt
done
Much clearer now, huh? :)

Setting variables in shell script by running commands

>cat /tmp/list1
john
jack
>cat /tmp/list2
smith
taylor
It is guaranteed that list1 and list2 will have equal number of lines.
f(){
i=1
while read line
do
var1 = `sed -n '$ip' /tmp/list1`
var2 = `sed -n '$ip' /tmp/list2`
echo $i,$var1,$var2
i=`expr $i+1`
echo $i,$var1,$var2
done < $INFILE
}
So output of f() should be:
1,john,smith
2,jack,taylor
But getting
1,p,p
1+1,p,p
If i replace following:
var1 = `sed -n '$ip' /tmp/list1`
var2 = `sed -n '$ip' /tmp/list2`
with this:
var1=`head -$i /tmp/vip_list|tail -1`
var2=`head -$i /tmp/lb_list|tail -1`
Then output:
1,john,smith
1,john,smith
If you can use paste and awk command, you can achieve the same with a one-liner:
paste -d, /tmp/list1 /tmp/list2 | awk '{print NR "," $0}'
Replace the while script with this line :)
the $ip is the problem there making ip the name of the variable, you should use ${i}p instead letting the shell know that the variable is i not ip, your code should look like
var1=`sed -n "${i}p" /tmp/list1`
var2=`sed -n "${i}p" /tmp/list2`

how to insert new line in bash shell for variable?

I have two variables var1 and var2. The contents of each variables come from bash shell grep command.
echo $var1 prints
123 465 326 8080
echo $var2 prints
sila kiran hinal juku
Now I want to print the above into following formats in Linux bash shell
123 sila
465 kiran
326 hinal
8080 juku
So how can I print this way in bash shell??
What about?
$ paste -d" " <(echo $var1 | xargs -n1) <(echo $var2 | xargs -n1)
We can even skip the echo:
$ paste -d" " <(xargs -n1 <<< $var1) <(xargs -n1 <<< $var2)
Without a loop:
$ var1="123 465 326 8080"
$ var2="sila kiran hinal juku"
$ var1=($var1); var2=($var2)
$ saveIFS=IFS
$ IFS=$'\n'
$ paste <(echo "${a[*]}") <(echo "${b[*]}"
$ IFS=$saveIFS
With a loop (assumes that the two strings have the same number of words):
$ var1="123 465 326 8080"
$ var2="sila kiran hinal juku"
$ var2=($var2)
$ for s in $var1; do echo $s ${vars[i++]}; done
Using file descriptors and a while-loop:
var1="123 465 326 8080"
var2="sila kiran hinal juku"
IFS=" " exec 7< <(printf "%s\n" $var1) 8< <(printf "%s\n" $var2)
while read -u7 f1 && read -u8 f2; do
echo "$f1 $f2"
done
I'd store them into arrays $var1[] and $var2[] instead of a some long string and then iterate through the arrays with a loop to output it the way you want.
If you don't want ot use arrays, you could use awk and the iterator from a loop to print out the names one at a time.
join <(echo $var1 | sed -r 's/ +/\n/g' | cat -n) <(echo $var2 | sed -r 's/ +/\n/g' | cat -n) -o "1.2,2.2"

Resources