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

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[#]}"

Related

I am not able to Ping IPs from my indexed array but works with an associative array

I am trying to iterate IPs which are read from a csv to an array as a kind of monitoring solution. I have the ips in a indexed array and want to pass the ips to the ping command but its not working.
#!/bin/bash
datei=hosts.csv
length=$(cat $datei | wc -l)
for (( i=1; i<=$length; i++ ))
do
ips[$i]=$(cut -d ';' -f2 $datei | awk 'NR=='$i'')
hosts[$i]=$(cut -d ';' -f1 $datei | awk 'NR=='$i'')
done
servers=( "1.1.1.1" "8.8.4.4" "8.8.8.8" "4.4.4.4")
for i in ${ips[#]} #Here i change the array i want to iterate
do
echo $i
ping -c 1 $i > /dev/null
if [ $? -ne 0 ]; then
echo "Server down"
else
echo "Server alive"
fi
done
Interesting is that if I iterate the server array instead of the ips array it works. The ips array seems from data fine if printed.
The output if I use the servers array:
1.1.1.1
Server alive
8.8.4.4
Server alive
8.8.8.8
Server alive
4.4.4.4
Server down
and if I use the ips array
1.1.1.1
: Name or service not known
Server down
8.8.4.4
: Name or service not known
Server down
8.8.8.8
: Name or service not known
Server down
4.4.4.4
: Name or service not known
Server down
Output from cat hosts.csv
test;1.1.1.1
test;8.8.4.4
test;8.8.8.8
test;4.4.4.4
First column is for Hostnames and the second column for the IPs in v4.
I am on Ubuntu 20.4.1 LTS
Fixed multiple issues with your code:
Reading of hosts.csv into arrays.
Testing the ping result.
hosts.csv:
one.one.one.one;1.1.1.1
dns.google;8.8.4.4
dns.google;8.8.8.8
invalid.invalid;4.4.4.4
Working commented in code:
#!/usr/bin/env bash
datei=hosts.csv
# Initialize empty arrays
hosts=()
ips=()
# Iterate reading host and ip in line records using ; as field delimiter
while IFS=\; read -r host ip _; do
hosts+=("$host") # Add to the hosts array
ips+=("$ip") # Add to the ips array
done <"$datei" # From this file
# Iterate the index of the ips array
for i in "${!ips[#]}"; do
# Display currently processed host and ip
printf 'Pinging host %s with IP %s\n' "${hosts[i]}" "${ips[i]}"
# Test the result of pinging the IP address
if ping -nc 1 "${ips[i]}" >/dev/null 2>&1; then
echo "Server alive"
else
echo "Server down"
fi
done
You can read into an array from stdin using readarray and so combining this with awk to parse the Host.csv file:
readarray -t ips <<< "$(awk -F\; '{ print $2 }' $date1)"
readarray -t hosts <<< "$(awk -F\; '{ print $1 }' $date1)"

awk printing random "e" that is not in string

I am wanting to extract ip and port from a string.
Strings look like this.
destination x.x.x.x:yyyy
where x is ip and y is port
commandout=()
while IFS= read -r line # Read a line
do
commandout+=("$line") # Append line to the array
done < <(tmsh list ltm virtual $vip | grep destination)
for output in "$commandout";
do
if [[ $output == *"destination"* ]];then
#split off ip and port
ipport=$(echo $output | awk 'BEGIN{}{print $2}')
echo $ipport | awk 'BEGIN{FS=":"}{print $1}'
echo $ipport
fi
done
declare -p commandout
for some reason, awk is printing a random "e" after the ip address. But it only appears to do so after 2.
10.10.10.10
10.10.10.10:https
declare -a commandout='([0]=" destination 10.10.10.10:https")'
12.12.12.12e
12.12.12.12:https
declare -a commandout='([0]=" destination 12.12.12.12:https")'
UPDATE:
So I attempted another test. I found strange behavior and I am unsure how to fix it.
I declare the vipip before and after it is set.
declare -p vipip
vipip=$(tmsh list ltm virtual $vip | grep destination | awk 'BEGIN{}{print $2}' | awk 'BEGIN{FS=":"}{print $1}')
echo $vipip
declare -p vipip
echo "cyle loop"
results in the following. Note that the 12.12.12.12 doesn't have an "e" on the end of it
./findvips-final.scr: line 240: declare: vipip: not found
10.10.10.10
declare -- vipip="10.10.10.10"
cyle loop
declare -- vipip="10.10.10.10"
12.12.12.12
declare -- vipip="12.12.12.12"
cyle loop
If I comment out the declare statements, I get an "e"
#declare -p vipip
vipip=$(tmsh list ltm virtual $vip | grep destination | awk 'BEGIN{}{print $2}' | awk 'BEGIN{FS=":"}{print $1}')
echo $vipip
#declare -p vipip
echo "cyle loop"
results in
10.10.10.10
cyle loop
12.12.12.12e
cyle loop
I found the answer. I have a progress meter above this and I was getting the e off of complete.
echo -ne "$((100*$z/$count))% Complete\r"
I wrapped $vipip in qoutes on the echo and it is working like I thought. UGh wait a big waste of time.
You can straightaway set FS like below to extract Ip from your command, no need of loop, awk can search string also
your_command | awk -F'[ :]' '/destination/{gsub(/[^0-9.]/,"",$2); print $2}'
Explanation
-F'[ :]' - set field separator
'/destination/ - search for word destination in line/record/row
gsub(/[^0-9.]/,"",$2) - remove anything other than number and dot from second field ( so that random char like e, what you said above will be removed )
print $2 - print second field

take ping test average change output

Here is my script I wanto change out put second one:
#!/bin/bash
declare -a arr=("8.8.8.8" "8.8.4.4" "192.168.1.28")
x=0
DATE=`date +%Y-%m-%d:%H:%M:%S`
echo $DATE > denemesh.txt
while [ $x -le 2 ]
do
echo " ${arr[x]}" >> denemesh.txt
ping -c 4 ${arr[x]} | tail -1| awk ' {print $4 }' | cut -d '/' -f 2 >> denemesh.txt
x=$(( $x + 1 ))
done
Currently, the output looks like this:
2014-12-22:20:22:37
8.8.8.8
18.431
8.8.4.4
17.758
192.168.1.28
0.058
Is it possible to change to output to look like this instead?
2014-12-22:20:22:37
8.8.8.8 18.431
8.8.4.4 17.758
192.168.1.28 0.058
You really just need to modify one line:
echo -n " ${arr[x]}" >> denemesh.txt
Using the -n flag suppresses the trailing newline, and so your next statement should append to the current line. You can then adjust the formatting as you please.
Sure it is. Try something like this:
declare -a arr=("8.8.8.8" "8.8.4.4" "192.168.1.28")
d=$(date +%Y-%m-%d:%H:%M:%S)
echo "$d" > denemesh.txt
for ip in "${arr[#]}"
do
printf ' %-12s' "$ip"
ping -c 4 "$ip" | awk 'END{split($4,a,"/"); printf "%12s\n", a[2]}'
done >> denemesh.txt
I've used printf with format specifiers to align the output. The %-12s left-aligns the first column with a fixed width of 12 characters and the %12s in awk right-aligns the second column. Rather than use a while loop, I got rid of your variable x and have looped through the values in the array directly. I have also changed the old-fashioned backtick syntax in your script to use $( ) instead. awk is capable of obtaining the output directly by itself, so I removed your usage of tail and cut too. Finally, you can simply redirect the output of the loop rather than putting >> on the end of each line.

Set variable from awk while parsing lines from a multiline file

I've got a txt file with several lines, each one describing a remote server, like this:
user#server:port:remote_working_path:whether_using_VPN
The : char separates the 4 fields.
I need to operate batch actions within each server, hence I need to parse each line and set appropriate variables. Right now, what I've coded is this:
while read server;
do
echo "$server" | awk -F ':' '{print $1}' &&
echo "$server" | awk -F ':' '{print $2}' &&
echo "$server" | awk -F ':' '{print $3}'
echo "$VPN"
declare $( echo "$server" | awk -F ':' '{print $VPN=$4}' )
echo 'VPN: '$VPN
done < $CUSTOMER_SERVERS_FILE
This script only prints the first 3 fields, and in my intentions should also set $VPN variable as the 4th field. However this seems way broken, and I'm being unable to fix it. How should I modify it so that $VPN = $4?
First, you don't need to use awk in this case. You could try to use something like :
while IFS=':' read -ra array; do
# "${array[0]}" => first field
# "${array[1]}" => second field
# ...
# "${array[#]}" => all fields
done < "$CUSTOMER_SERVERS_FILE"
Then if you want to set VPN variable with the 4th field, you could use :
while IFS=':' read -ra array; do
# ...
VPN="${array[3]}"
done < "$CUSTOMER_SERVERS_FILE"
Another solution :
while IFS=':' read -r address port path vpn trash; do
# The variables $adress $port $path and $vpn are assigned.
# $trash is set with other fields if there are more than 4 fields
done
Finally, when you want to assign the output of a command in a variable, you could do :
var="$(command)"
# or
var="`command`"

Building Dynamic Variable Names in KornShell

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

Resources