Bash grep file for string and use each as a variable in another commands - bash

In HA configuration I am checking periodically for VIP address on eth0, (lets call it 2.2.2.2). If it is up, then I need to bring up another group of IP address defined for eth0 in /etc/network/interfaces configuration file:
up ip addr add **1.2.3.34** dev $IFACE
up ip addr add **1.2.3.40** dev $IFACE
up ip addr add **1.2.3.48** dev $IFACE
and pass each IP only to another group of commands:
ip a a **1.2.3.34/32** dev eth0
ip a a **1.2.3.40/32** dev eth0
ip a a **1.2.3.48/32** dev eth0
What I've done so far is:
#!/bin/bash
STATUS=$(ip a s eth0 | grep inet | awk '{print $2}' | sed 's/addr://')
if ip a s eth0 | grep inet | awk '{print $2}' | sed 's/addr://' | grep 2.2.2.2/27 ; then
cat /etc/network/interfaces | grep -o "up ip addr add [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | grep -o "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" > /tmp/ext_ip.txt
Now I need help how to pass each line to another command mentioned above (ip a a 1.2.3...), but have no idea how to do this properly.
Optionally I'd like to revert operation if VIP is not present in the system - in case if primary HA host will go offline.

One way to achieve this would be to parse all IP addresses into an array and use a for loop to assign them to the interface:
#!/bin/bash
IFACE='eth0'
VIP='192.168.0.1'
IFACES_FILE='/etc/network/interfaces'
STATUS=$( ip address show "$IFACE" | grep -o "$VIP" )
if [ ! -z "$STATUS" ]; then
ip_addresses=( $( grep -o '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' "$IFACES_FILE" ) )
for ip in "${ip_addresses[#]}"; do
ip address add "$ip" dev "$IFACE"
done
fi
This is a simplified example. You may want to add more checks, add some logging messages to provide output useful for debugging. Also, depending on your server configuration some commands may not work without sudo.

Thanks Andrii L. I've improved your solution to:
#!/bin/bash
IFACE='eth0'
VIP='2.2.2.2'
IFACES_FILE='/etc/network/interfaces'
STATUS=$( ip address show "$IFACE" | grep -o "$VIP" )
if [ ! -z "$STATUS" ]; then
ip_addresses=( $( grep -o 'up ip addr add [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' "$IFACES_FILE" | grep -o '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' ) )
for ip in "${ip_addresses[#]}"; do
ip address add "$ip"/32 dev "$IFACE"
done
else
for ip in "${ip_addresses[#]}"; do
ip address del "$ip"/32 dev "$IFACE"
done
fi
otherwise it is trying to add all IPs and broadcasts found in /etc/network/interfaces file.

Related

Makefile evaluate 2 variables then use the non-empty one

I have a TV box running CoreELEC (a Linux distro similar to LibreELEC) and I would like to find its IP address to then use SSH to log into it.
I know the MAC addresses of both the Ethernet interface and the Wifi interface.
I want to find the IP address of my TV box in my LAN (either Ethernet or Wifi) assuming I don't know which interface is currently used by the TV box to connect to the LAN.
The following list of targets does not work as it fails when evaluating the "OR":
TVBOX_IP=""
find-tvbox-eth-ip:
#echo Checking the Ethernet IP address...
$(eval TVBOX_ETH_IP=$(shell sudo arp-scan 192.168.0.1/24 2>&1 | grep $(TV_ETH_MAC) | awk '{print $$1}'))
#echo Found an Ethernet IP address: "$(TVBOX_ETH_IP)"
find-tvbox-wifi-ip:
#echo Checking the Wifi IP address...
$(eval TVBOX_WIFI_IP=$(shell sudo arp-scan 192.168.0.1/24 2>&1 | grep $(TV_WLAN_MAC) | awk '{print $$1}'))
#echo Found a Wifi IP address: "$(TVBOX_WIFI_IP)"
find-tvbox-ip: find-tvbox-eth-ip find-tvbox-wifi-ip
#[ "$(TVBOX_ETH_IP)" ] && TVBOX_IP:=$(TVBOX_ETH_IP) || ( echo TVBOX_IP:=$(TVBOX_WIFI_IP) )
#echo "Stored this IP for the TV BOX: " $(TVBOX_IP)
A similar target containing TVBOX_IP := $(or $(TVBOX_ETH_IP),$(TVBOX_WIFI_IP)) does not work as well.
How can I use the variable $(TVBOX_IP) in other targets after making sure their value come first from the Ethernet check, then from the Wifi check?
What's wrong with the Makefile targets above?
Sometimes, it is easier to move logic to external commands. Make declarative approach is sometimes hard to control:
Consider the alternative: Moving the conditional to the shell.
# Notice this is definition, will not force immediate evaluation
TVBOX_ETH_IP= sudo arp-scan 192.168.0.1/24 2>&1 | grep $(TV_ETH_MAC) | awk '{print $$1}'
TVBOX_WIFI_IP=sudo arp-scan 192.168.0.1/24 2>&1 | grep $(TV_WLAN_MAC) | awk '{print $$1}'
find-tvbox-ip:
IP=$$(${TVBOX_ETH_IP}) ; [ "$$IP" ] || IP=$$(${TVBOX_WIFI_IP}) ; echo "IP=$$IP"
Or keeping the condition in make $(or ...)
TVBOX_ETH_IP=$(shell sudo arp-scan 192.168.0.1/24 2>&1 | grep $(TV_ETH_MAC) | awk '{print $$1}')
TVBOX_WIFI_IP=$(shell sudo arp-scan 192.168.0.1/24 2>&1 | grep $(TV_WLAN_MAC) | awk '{print $$1})'
TVBOX_IP=$(or ${TVBOX_ETH_IP}, ${TVBOX_ETH_IP})
find-tvbox-ip:
IP=${TVBOX_IP} ; echo "IP=$$IP"

Shell Script IP Neighbor Refresh

I am working on an application of multiple devices in the same network. One of them takes the lead and performs some functions. I arbitrate the "lead" by the highest IP I find.
My application is intended to be fault-tolerant and if I loose the controller, next in the line should take over.
I am running the script below.
#define SHELLSCRIPT "\
#/bin/bash \n\
echo $(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1) > IP.txt\n\
while read LINE; do echo $LINE | grep -i -E 192.168 | grep -v .254 | cut -c1-13; done < /proc/net/arp >> IP.txt\n\
"
The problem I have found is that the ARP cache does not refresh automatically. Does anyone has any idea how to "refresh" the ARP table (without using arp commands).
I have already tried "ip -s neigh flush all", but it seems to have a delay to refresh the ARP table.
Anyway, I would appreciate any suggestion.
You can disable and enable the use of the ARP protocol on interface at the same time.
ARP cache will be cleared quitly fast.
ip link set arp off dev eth0 ; ip link set arp on dev eth0
Please, at first run this on test environment for checking and be sure there no connection interruption.

Processing ifconfig and hostname output and frame it in single line for multiple hosts

I'm trying to filter remote machine mac address , IP and hostname from 100 machines. Initially I'm capturing the information using below command.
$for i in `cat IP`;do in ssh root#$IP "ifconfig eth0 && hostname " ;done
eth0 Link encap:Ethernet HWaddr 00:MN:77:TR:XX:ZZ
inet addr:192.168.122.25 Bcast:192.168.122.255
Mask:255.255.224.0
test.com-112304
eth0 Link encap:Ethernet HWaddr 00:TT:77:MM:XX:YY
inet addr:192.168.122.22 Bcast:192.168.122.255
Mask:255.255.224.0
test.com-11035
Initially the output redirect to one file. from their I need to process output which comes in the above format.
I can able to accomplish to print IP and mac by line as follows
$cat input | awk '/HWaddr/{printf $NF;printf " ";getline;print $2;}' | cut -c1-18,24-
00:MN:77:TR:XX:ZZ 192.168.122.25
00:TT:77:MM:XX:YY 192.168.122.22
Actually I want to include hostname along with the output as follows
00:MN:77:TR:XX:ZZ 192.168.122.25 test.com-112304
00:TT:77:MM:XX:YY 192.168.122.22 test.com-11035
test.com will be common name for all hostname. I tried redirect both output in two variable and called as follows , but no luck.
a=`cat input | awk '/HWaddr/{printf $NF;printf " ";getline;print $2;}' | cut -c1-18,24-`
b=`grep test.com input`
echo $a $b | xargs -n2
Please shed some views
I'm trying to filter remote machine mac address , IP and hostname from 100 machines.
Machine mac address, from here: cat /sys/class/net/eth0/address
You have hostname utility, why don't you just hostname -i? You can parse ifconfig | grep inet | awk '{print $2}' | sed 's/addr://' and hope ifconfig versions are the same across all machines (some ifconfig verions return inet <ip> some return inet addr:<ip> like yours).
Hostname with hostname
Putting together:
echo $(cat /sys/class/net/eth0/address) $(ifconfig eth0 | grep inet | awk '{print $2}') $(hostname)
Executing a command for each line in some file is a job for xargs and remember about properly escaping your arguments:
cat IP | xargs -n1 -I{} ssh root#{} 'echo $(cat /sys/class/net/eth0/address) $(ifconfig eth0 | grep inet | awk '\''{print $2}'\'' | sed '\''s/addr://'\'') $(hostname)'
If you really have 100 machines, ansible might interest you.
If you really have that "input" file and you can't change it, you may parse it like this:
sed 's/eth0/#eth0/' input \
| xargs -d'#' -n1 -- bash -c '{ echo "$1" | grep HWaddr | awk "{print \$5}"; echo "$1" | grep inet | awk "{print \$2}" | sed "s/addr://"; echo "$1" | grep "^test.com"; } | tr "\n" " "; echo' --
And this hurts my eyes:
for i in `cat IP`; then

use tcpkill command from an ip list

I have got an ip list in a file called ips:
ip1
ip2
I want to tcpkill from this list. I cannot do it from tcpkill command options. I need a script which will write :
tcpkill -i eth0 -9 host ip1 or host ip2
the list can grow in time.
the only thing I know is that I will need a loop to read the list:
for IP in $(cat list) ; do
echo "tcpkill -i eth0 -9 host $IP " (for
the first element of the list)
echo "or host $IP" (for the rest of
the list).
So my questions are:
1. what would be the right syntax for this purpose?
2. Is there a completely different way of doing this?
Any idea folks ?!
thank you very much.
This single line command would append the IP list to tcpkill command to form the complete command:
tcpkill -i eth0 -9 host $(cat list | xargs | sed 's/ / or host /g')
You can put this command after echo to see how the command gets formed.
echo tcpkill -i eth0 -9 host $(cat list | xargs | sed 's/ / or host /g')

In a shell script how to validate if a given host name belongs to localhostname family

In a shell script how to validate if a given host name is localhost
for example :-
localhost
127.0.0.1
myhostname.com
::1
all belong to same machine name, Is there any way to identify that a given host name belongs to localhostname family
Usually all local host names are in /etc/hosts file:
grep -c machine_name /etc/hosts
if the machine name is among the localhost the command above returns 1 or greater, otherwise is 0.
for example:
grep -c myhostname.com /etc/hosts
1
grep -c google.com /etc/hosts
0
Not sure if this is exactly you're looking for but I hope it will help.
Beware of partial match, for example if you have 'myhost' in /etc/hosts grep -c host will return 1 as well. In this case you'll need to use regular expressions or parse /etc/hosts file with cut, awk and similar tools. Or use the following command:
grep -c '\bmachine name\b'
To skip the comments use the command below:
grep -v '^#.*' /etc/hosts | grep -c machine_name
so full command is
grep -v '^#.*' /etc/hosts | grep -c '\bmachine_name\b'
You could check
sysctl kernel.hostname
i.e
sysctl kernel.hostname | grep -c "my_hostname"
I use the following to check whether a supplied hostname is the same as localhost:
hostname_ip(){
host "$1" | sed -e 's/.* \([^ ]*[^ .]\)\.*$/\1/'
}
normalize_hostname(){
local normalized="$1"
grep -q "^\(\([0-9]{1,3}\)\.\)\{3\}\([0-9]{1,3}\)$" <<< "$normalized" || normalized="$(hostname_ip "$normalized")"
normalized="$(hostname_ip "$normalized")"
echo "$normalized"
}
myname="$(normalize_hostname "$(hostname)")"
argname="$(normalize_hostname "$1")"
if [[ "$myname" == "$argname" || "$argname" == "localhost" ]]; then
...
First, normalize the supplied parameter into format set by host utility by running it twice. If IP address is supplied -- checked by regex -- run it only once.
Then compare the value to normalized value of hostname utility or to string "localhost".

Resources