Bash Replace Variable IP Address with Network - bash

I have an IP address set in a variable that I'd like to convert into a network address.
This only works for a single digit:
echo '192.168.1.2' | sed 's/.$/0/' => 192.168.1.0
echo '192.168.1.22' | sed 's/.$/0/' => 192.168.1.20
echo '192.168.1.223' | sed 's/.$/0/' => 192.168.1.220
I need a method to return the same network value if the last digit(s) change, i.e:
myip="192.168.1.2" => "192.168.1.0"
myip="192.168.1.22" => "192.168.1.0"
myip="192.168.1.223" => "192.168.1.0"
How can I replace any IP address with it's network address like above?

Pure bash solution without external commands:
echo "${myip%.*}.0"
for example:
$ echo "$myip"
192.168.1.22
$ echo "${myip%.*}.0"
192.168.1.0

Using sed
echo '192.168.1.2' | sed 's/\.[^.]*$/.0/'
sed 's/\.[^.]*$/.0/' <<< 192.168.1.22 # echo + pipe is not needed here
Logic: Replace everything from last . till end with .0
Using awk
awk -F. '{$NF=0}1' OFS=. <<< 192.168.1.22
awk '{$NF=0}1' FS=. OFS=. <<< 192.168.1.22
Logic: Split string with . and set last field to 0.
pure bash:
{ IFS=. read a b c _; echo $a.$b.$c.0; } <<< 192.168.1.22
( IFS=.; read -a ip; ip[3]=0; echo "${ip[*]}"; ) <<< 192.168.1.22
Logic: Read 4 parts of the IP address in 4 variables. Print first 3 and a 0.
Or by using a bash array, if you don't want to clutter environment with too many variables.

You can do this with awk using:
pax> awk -F. '{print $1"."$2"."$3".0"}' <<<12.34.56.78
12.34.56.0
With sed, it's possible to just replace all the digits at the end:
pax sed 's/[0-9]*$/0/' <<<12.34.56.78
12.34.56.0
However, all of those result in an extra process being started up, not something you need to worry about for a few IP addresses but it will make a difference if you're converting many of them.
To do it within bash only (not requiring another process), you can use:
pax> ip=12.34.56.78
pax> echo ${ip%.[0-9]*}.0
12.34.56.0

It is very simple to do with pure bash:
myip="192.168.1.2 "; echo "$myip ==> ${myip%.*}.0"
myip="192.168.1.22 "; echo "$myip ==> ${myip%.*}.0"
myip="192.168.1.223"; echo "$myip ==> ${myip%.*}.0"
Results in:
192.168.1.2 ==> 192.168.1.0
192.168.1.22 ==> 192.168.1.0
192.168.1.223 ==> 192.168.1.0
However, that is assuming the network has a CDIR of 24 (192.168.1.2/24).
If that is not what you will always use, this idea will break.

Related

BASH IF statement false negative

I am having some problems comparing a variable from a script to the IP address of the system. I have a source file for an app which is not bash fiendly but it has a value set like so
DKUS_MASTER=127.0.0.1
I am fetching that variable in my bash file by doing
DKUSMASTER=`grep "DKUS_MASTER" /root/somestuff.conf | sed 's/DKUS_MASTER=//'`
Here is what i am then doing in my script as i am trying to see if eth0 ip is set to the DKUS_MASTER parameter i am fetching
DKUSMASTER=`grep "DKUS_MASTER" /root/somestuff.conf| sed 's/DKUS_MASTER=//'`
ETH0=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
if [ "$ETH0" = "$DKUSMASTER" ]; then
DKUS_STATUS="Master"
else
DKUS_STATUS="Slave"
fi
doing some testing, i can see the variables look correct
echo $DKUSMASTER
echo $ETH0
however, the end status is else false and never true as seen here. In my case the variable $DKUSMASTER does indeed equal to $ETH0, so the status should come back as master.
echo $DKUS_STATUS

How to save to the var, only one from the output

Im writing a script that executes dig command on 2 domains, and after next cmd is host on output.
And always i will get for exmaple:
findUserServer=for r in $(dig +short $login.example.COM && dig +short $login.example.ORG); do host $r|awk '{print $NF}';done | awk -F "." '{print $1}';
1 output: >> asdf02 example
asdf02 - its a server name, its always same name starts "asdf".
Question: Have you any idea how to save to the variable only asdf02?
question+: asdf02 woudln't be always first, could be example asdf02
Should i do maybe a sed which looks on 4 first characters? If it's "asdf", then: [...]
Try not to pipe awk commands into each other and so:
for r in $(dig +short $login.example.COM && dig +short $login.example.ORG); do host $r;done | awk -F [.\ ] '/asdf02/ { print $10 }'
We use both a space and . as delimiters and then pattern match the output for the occurance of asdf02. If we find is, we print the address.
Run that through shellcheck.net ...
Try this.
findUserServer="$( for end in COM ORG; do
host $( dig +short $login.example.$end );
done | sed -n '/ asdf/{ s/^.* //; s/[.].*//; p; }' )"
This will run 2 digs and pipe the collective output through sed,
which will ignore lines that don't have asdf, and strip the matches clean for you.
Let me know if I missed details, because I don't have those exact values available.

Bash - Convert netmask in CIDR notation?

Example:
I have this netmask: 255.255.255.0
Is there, in bash, a command or a simple script to convert my netmask in notation
/24?
Function using subnetcalc:
IPprefix_by_netmask() {
subnetcalc 1.1.1.1 "$1" -n | sed -n '/^Netw/{s#.*/ #/#p;q}'
}
In pure bash, (i.e. no external utils like sed or bc), convert IP to a long octal string and sum its bits:
IPprefix_by_netmask () {
c=0 x=0$( printf '%o' ${1//./ } )
while [ $x -gt 0 ]; do
let c+=$((x%2)) 'x>>=1'
done
echo /$c ; }
Output of IPprefix_by_netmask 255.255.255.0 (either function):
/24
Example Function for RHEL6/RHEL7:
IPprefix_by_netmask() {
#function returns prefix for given netmask in arg1
ipcalc -p 1.1.1.1 $1 | sed -n 's/^PREFIX=\(.*\)/\/\1/p'
}
The Result:
$ IPprefix_by_netmask 255.255.255.0
/24
In other Linux distributives ipcalc options may differ.
The same function without ipcalc, tested in Solaris and Linux:
IPprefix_by_netmask() {
#function returns prefix for given netmask in arg1
bits=0
for octet in $(echo $1| sed 's/\./ /g'); do
binbits=$(echo "obase=2; ibase=10; ${octet}"| bc | sed 's/0//g')
let bits+=${#binbits}
done
echo "/${bits}"
}
Solution based on awk
While GNU awk is not Bash, it’s installed by default in enough distributions that this may be helpful in the sense of the question:
awk -F. '{
split($0, octets)
for (i in octets) {
mask += 8 - log(2**8 - octets[i])/log(2);
}
print "/" mask
}' <<< 255.255.255.240
This prints:
/28
Based on Sasha's answer, this script works with dash (tested with Ubuntu 18.04):
IPprefix_by_netmask() {
#function returns prefix for given netmask in arg1
bits=0
for octet in $(echo $1| sed 's/\./ /g'); do
binbits=$(echo "obase=2; ibase=10; ${octet}"| bc | sed 's/0//g')
bits=$(expr $bits + ${#binbits})
done
echo "/${bits}"
}

shell script pattern matching

Need help on shell script.
I have following result in a file
name-label ( RW) : host1
networks (MRO): 1/ip: 192.168.1.2; 0/ip: 192.168.1.10
name-label ( RW) : host2
networks (MRO): 1/ip: 192.168.1.15; 1/ipv6/0: fe80::9060:b6ff:fec1:7bbb; 0/ip: 192.168.1.20; 0/ipv6/0: fe80::286d:7cff:fefe:3ed7
I want only the hostname and corresponding 0/ip value from file. Final output will be
host1 192.168.1.10
host2 192.168.1.20
Perl solution:
perl -ne '/^name-label .*: (.+)/ and $name = $1; m(0/ip: ([0-9.]+)) and print "$name $1\n"'
Name-label is stored in a variable, it's printed with the IP when processing the next line.
#!/bin/bash
grep ") :" FILENAME | sed 's/.*) ://' | sed 's/networks.*ip://' | sed 's/;.*//'
FILENAME is your file.

Reverse order of a string

I want to "reverse" the order of the four octets (bytes) that make up an ip address.
Suppose I have this ip:
202.168.56.32
I need to convert into:
32.56.168.202
and then ultimately remove the first octet in the reversed ip. Final result:
56.168.202
My attempts:
echo 202.168.56.32 | rev
But it's returning :
23.65.861.202
This should do the trick:
echo 202.168.56.32|awk -F. '{print $3"."$2"."$1}'
You could also do it with bash arrays:
ip=202.168.56.32
parts=(${ip//./ })
echo ${parts[2]}.${parts[1]}.${parts[0]}
Or you could use sed.
echo 202.168.56.32 | sed -e 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\4.\3.\2.\1/g'

Resources