Bash script match ipv4 addresses with regex variable - bash

Trying to get only the lines with ipv4 addresses in the $networks variable.
#!/bin/bash
ivp4_pattern='/^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/igm'
networks=$(ip addr | grep "inet" | awk '{print $2}')
while read -r line;
do
echo "$line"
done <<< "$networks"
echo "$ivp4_pattern"
echo "$networks" | grep "$ivp4_pattern"
Output:
[jonathan#localhost ~]$ ./script.sh
127.0.0.1/8
::1/128
172.16.155.128/24
fe80::da84:977a:d654:7716/64
/^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/igm
Tried removing the / and with -E...
#!/bin/bash
ivp4_pattern="'^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$'"
networks=$(ip addr | grep "inet" | awk '{print $2}')
while read -r line;
do
echo "$line"
done <<< "$networks"
echo $ivp4_pattern
echo $networks | grep -E $ivp4_pattern
Also tried loop through networks line by line and taking the regex out of the variable...
#!/bin/bash
networks=$(ip addr | grep "inet" | awk '{print $2}')
while read -r line;
do
echo "$line"
done <<< "$networks"
while read -r line;
do
echo $line
echo $line | grep '^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$'
done <<< "$networks"
I got it working adding -E to grep without regex in variable...but why is it working? It doesn't like the regex being in a variable?
#!/bin/bash
networks=$(ip addr | grep "inet" | awk '{print $2}')
while read -r line;
do
echo "$line"
done <<< "$networks"
while read -r line;
do
# echo $line
echo $line | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$'
done <<< "$networks"
~

As a bash solution, how about:
ipv4_pattern="([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
ip addr | while read -r line; do
if [[ $line =~ inet\ $ipv4_pattern ]]; then
echo "${BASH_REMATCH[1]}"
fi
done
Note that the while loop above is invoked in the child process and
variables assigned here are inaccessible from the parent.
In such a case, please make use of a process substitution as;
ipv4_pattern="([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
while read -r line; do
if [[ $line =~ inet\ $ipv4_pattern ]]; then
echo "${BASH_REMATCH[1]}"
# do some assignments here as ip_list+=("${BASH_REMATCH[1]}")
fi
done < <(ip addr)
Hope this helps.

If you want to list only IPv4 addresses, how about this?
The first ...
#!/bin/bash
#
# ip addr : list IP info.
# grep "inet " : Only for IPv4. IPv6 addresses are listed up with "inet6".
# awk '{print $2}': Extract IPv4 adress
ipaddrs=$(ip addr | grep "inet " | awk '{print $2}')
while read -r ipaddr
do
echo $(cut -d"/" -f1 <<< ${ipaddr})
done <<< "${ipaddrs}"
exit 0
The Second is ...
#!/bin/bash
#
# ip addr : list IP info.
# grep "inet " : Only for IPv4. IPv6 addresses are listed up with "inet6".
ipaddrs=$(ip addr | grep "inet ")
while read -r ipaddr
do
# sample: inet 100.52.62.173/24 brd 100.52.62.255 scope global bond1
ipv4_cidr=$(cut -d" " -f2 <<< ${ipaddr})
ipv4=$(cut -d"/" -f1 <<< ${ipv4_cidr})
netmask=$(cut -d"/" -f2 <<< ${ipv4_cidr})
brdcast=$(cut -d" " -f4 <<< ${ipaddr})
echo "----------------------------"
echo "============================"
echo "ip addr : ${ipaddr}"
echo "----------------------------"
echo "IP Address: ${ipv4}"
echo "Netmask : ${netmask}"
echo "Broadcast : ${brdcast}"
done <<< "${ipaddrs}"
exit 0
The third is ...
#!/bin/bash
#
# ip addr : list IP info.
# grep "inet " : Only for IPv4. IPv6 addresses are listed up with "inet6".
ipaddrs=$(ip addr | grep "inet ")
while IFS=" " read -ra ipaddr
do
# If a first line is "inet 100.52.62.173/24 brd 100.52.62.255 scope global bond1"
# 1. ipadddr is (inet 100.52.62.173/24 brd 100.52.62.255 scope global bond1)
# A second is cidr.
IFS="/" read -ra ipv4_cidr <<< "${ipaddr[1]}"
# 2. ipv4_cidr is ("100.52.62.173" "24")
ipv4="${ipv4_cidr[0]}"
netmask="${ipv4_cidr[1]}"
# A fourth is 'broadcast'.
brdcast="${ipaddr[3]}"
echo "----------------------------"
echo "============================"
echo "ip addr : ${ipaddr[#]}"
echo "----------------------------"
echo "IP Address: ${ipv4}"
echo "Netmask : ${netmask}"
echo "Broadcast : ${brdcast}"
done <<< "${ipaddrs}"
exit 0

Related

How can I convert my output to JSON then save it as.json file

Get System Information
Here is my script:
#!/bin/bash
echo -e "Manufacturer:\t"`cat /sys/class/dmi/id/chassis_vendor`
echo -e "Product Name:\t"`cat /sys/class/dmi/id/product_name`
echo -e "Version:\t"`cat /sys/class/dmi/id/bios_version`
echo -e "Serial Number:\t"`cat /sys/class/dmi/id/product_serial`
echo -e "PC Name:\t"`hostname`
echo -e "Operating System:\t"`hostnamectl | grep "Operating System" | cut
-d ' ' -f5-`
echo -e "Architecture:\t"`arch`
echo -e "Processor Name:\t"`awk -F':' '/^model name/ {print $2}'
/proc/cpuinfo | uniq | sed -e 's/^[ \t]*//'`
echo -e "Memory:\t" `dmidecode -t 17 | grep "Size.*MB" | awk '{s+=$2} END
{print s / 1024 "GB"}'`
echo -e "HDD Model:\t" `cat /sys/block/sda/device/model`
echo -e "System Main IP:\t"`hostname -I`
I Want to Display my Output like this
({"Manufacturer":"Lenovo","Product Name":"Thinkpad":"Version":"T590","Serial Number":"1234567890" })
Thanks in advance for your help!
Here is a pure bash option. escape takes care of " in either key or value. member generates a key: value pair, and members separate members with comma:
escape() {
echo -n "${1/\"/\\\"}"
}
member() {
echo -en "\"$(escape "$1")\":\"$(escape "$2")\"\x00"
}
members() {
local sep=''
echo -n "{"
while read -d $'\0' member
do
echo -n "${sep}$member"
sep=,
done
echo -n "}"
}
declare -A a=(
[Manufacturer]=`cat /sys/class/dmi/id/chassis_vendor`
[Product Name]=`cat /sys/class/dmi/id/product_name`
[Version]=`cat /sys/class/dmi/id/bios_version`
)
for k in "${!a[#]}"
do
member "$k" "${a[$k]}"
done | members > as.JSON
Store all the echo statements in one variable or yet file
this return an array of json
cat /path/to_your-text file | jq --raw-input . | jq --slurp

Sorting information and processing output with variables in Bash

I've received some helpful responses in the past and am hoping you can all help me out. I came across some weird behavior that I can't quite nail down. I'm processing configuration files for Cisco switches and want to generate output that lists the VLAN IP Addresses in a format that would show:
Vlan1: 172.31.200.1 255.255.255.0
Vlan10: 172.40.220.1 255.255.255.0
The "Vlan" would be captured in a variable and the IP/Mask is extracted using "sed" and it works for the most part. Occasionally though it refuses to populate the "vlan" variable even though it appears to work great for other configs.
If there's only one VLAN it just handles that one, if there's more than one it handles the additional ones. If the user selects (-v) it includes VLAN1 on the list there are multiple VLANs configured (otherwise it ignores VLAN1).
This input file appears broken (Filename 1.cfg):
!
interface Vlan1
ip address 172.29.96.100 255.255.255.0
!
ip default-gateway 172.29.96.1
ip http server
no ip http secure-server
!
This input file works fine (Filename 2.cfg):
!
interface Vlan1
ip address 172.31.200.111 255.255.255.0
no ip route-cache
!
ip default-gateway 172.31.200.1
ip http server
ip http secure-server
The output that I get is this:
Notice how the first one fails to include the "Vlan1" reference?
Here's my script:
#!/bin/bash
if [ -f getlan.log ];
then
rm getlan.log
fi
TempFile=getlan.log
verbose=0
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-v)
verbose=1
shift
;;
#*)
#exit
#shift
#;;
esac
done
#Start Processing all files
files=$( ls net )
for i in $files; do
#########################################################
# Collect Configured VLAN Interfaces and IP Information #
#########################################################
echo "-------- Configured VLAN Interfaces --------" >> ~/$TempFile
echo "" >> ~/$TempFile
if [ `grep "^interface Vlan" ~/net/$i | awk '{ print $2 }' | wc -l` -gt 1 ];
then
for g in `grep "^interface Vlan" ~/net/$i | awk '{ print $2 }'`;
do
if [ $g == "Vlan1" ];
then
if [ $verbose -gt 0 ];
then
echo "$g": `sed -n '/^interface '$g'/,/!/p' ~/net/$i | head -n 5 | grep -i "ip address" | awk '{ print $3, $4 }'` >> ~/$TempFile
fi
else
echo "$g": `sed -n '/^interface '$g'/,/^!/p' ~/net/$i | grep -i "ip address" | awk '{ print $3, $4 }'` >> ~/$TempFile
fi
done
echo "" >> ~/$TempFile
else
vlanid=`grep "^interface Vlan" ~/net/$i | awk '{ print $2 }'`
echo $vlanid: `sed -n '/^interface 'Vlan'/,/^!/p' ~/net/$i | grep -i "address" | awk '{ print $3, $4 }'` >> ~/$TempFile
echo "" >> ~/$TempFile
fi
done
It would be really great if this was more consistent. Thanks!
A best-practices approach might look more like:
#!/usr/bin/env bash
interface_re='^[[:space:]]*interface[[:space:]]+(.*)'
ip_re='^[[:space:]]*ip address (.*)'
process_file() {
local line interface
interface=
while IFS= read -r line; do
if [[ $line =~ $interface_re ]]; then
interface=${BASH_REMATCH[1]}
continue
fi
if [[ $interface ]] && [[ $line =~ $ip_re ]]; then
echo "${interface}: ${BASH_REMATCH[1]}"
fi
done
}
for file in net/*; do
process_file <"$file"
done
This can be tested as follows:
process_file <<'EOF'
!
interface Vlan1
ip address 172.29.96.100 255.255.255.0
!
ip default-gateway 172.29.96.1
ip http server
no ip http secure-server
!
!
interface Vlan1
ip address 172.31.200.111 255.255.255.0
no ip route-cache
!
ip default-gateway 172.31.200.1
ip http server
ip http secure-server
EOF
...which correctly identifies the ip address lines from both interface blocks, emitting:
Vlan1: 172.29.96.100 255.255.255.0
Vlan1: 172.31.200.111 255.255.255.0

Parsing in bash - extract content from brackets

The input to my bash script can be of the form [fec1::1]:80 or []:80. The second input implies that there's no IP address given. My bash script is to split the input into IP and port. With the said second input, the script should 'understand' that no IP was given.
The following logic seems to solve my problem, on the bash prompt:
$ ip=[]:78
$ echo $ip
[]:78
$ temp=(`echo $ip | awk -F'[][]' '{print $2}'`)
$ echo $temp
$
When I try to do the same thing from within a script, the result is different:
local_endpoint="$1"
printf 'local_endpoint: %s\n' "$local_endpoint"
IN="$local_endpoint"
local_ip=$(echo "$IN" | awk -F'[][]' '{print $2}')
if [ -z "$local_ip" ] ; then
local_ip=$(echo "$IN" | awk -F':' '{print $1}')
local_port=$(echo "$IN" | awk -F':' '{print $2}')
else
local_port=$(echo "$IN" | awk -F'[][]' '{print $3}' | awk -F':' '{print $2}')
fi
printf 'IP: %s\n' $local_ip
printf 'port: %d\n' $local_port
if [ -z "$local_port" -a -z "$local_ip" ] ; then
printf 'No port and IP was given\n'
elif [ -z "$local_ip" ] ; then
printf 'No IP was given\n'
elif [ -z "$local_port" ] ; then
printf 'No port was given\n'
fi
exit 2
Output:
# ./temp.sh []:829
local_endpoint: []:829
IP: []
port: 829
Any idea on what's happening? Also, why do I see the extra comma (,) at the end of the output?
Your script is missing quoting at many places and there are stray commas too in printf. This script should work:
local_endpoint="$1"
printf 'local_endpoint: %s\n' "$local_endpoint"
IN="$local_endpoint"
if [[ "$IN" == "["* ]] ; then
local_ip=$(echo "$IN" | awk -F'[][]' '{print $2}')
local_port=$(echo "$IN" | awk -F'[][]' '{print $3}' | awk -F':' '{print $2}')
else
local_ip=$(echo "$IN" | awk -F':' '{print $1}')
local_port=$(echo "$IN" | awk -F':' '{print $2}')
fi
printf 'IP: <%s>\n' "$local_ip"
printf 'port: <%d>\n' "$local_port"
if [ -z "$local_port" -a -z "$local_ip" ] ; then
printf 'No port and IP was given\n'
elif [ -z "$local_ip" ] ; then
printf 'No IP was given\n'
elif [ -z "$local_port" ] ; then
printf 'No port was given\n'
fi
exit 2
Process substitution is:
var=$(command ...)
not
var=(command ...)

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? :)

Bash error echo a command

I have a problem. I need to show a echo from a while, I use two echo the first one work but the second it give a error.
#!/bin/bash
conexiuni="/tmp/conexiuni"
if [ "$1" != "" ]; then
netstat -tuan | grep $1 | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n > $conexiuni
else
netstat -tuan | grep ESTAB | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n > $conexiuni
fi
cat $conexiuni | while read line
do
con=`echo ''$line'' | awk '{print $1}'`
ip=`echo ''$line'' | awk '{print $2}'`
if [ "$con" -gt "4" ]; then
`echo -e "$ip" >> /var/log/drop_sc_ip`
`echo -e "$ip"`
fi
done
if [ -f "$conexiuni" ];
then
`rm -rf $conexiuni`
fi
The error is :
./show_conn: line 15: 8.97.80.2: command not found
./show_conn: line 15: 8.76.109.13: command not found
./show_conn: line 15: 8.33.15.2: command not found
./show_conn: line 15: 9.118.226.3: command not found
You can write this part without the backticks:
if [ "$con" -gt "4" ]; then
echo -e "$ip" >> /var/log/drop_sc_ip
echo -e "$ip"
fi
also same in this part:
rm -rf $conexiuni
with the backticks, it first executes what is inside the backticks and then tries to execute the output of the backticks.
and change the loop:
while read con ip
do
if [ "$con" -gt "4" ]; then
echo -e "$ip" >> /var/log/drop_sc_ip
echo -e "$ip"
fi
done < $conexiuni

Resources