Replace IPs with Hostnames in a log - linux

I am looking for a bash script that reads a log and replaces IP addresses with a hostname. Does anyone have any idea of how to do this?

Following script should work. You can use it like this:
save it to ip_to_hostname.sh and then:
./ip_to_hostname.sh your_logfile > resolved_ip
#!/bin/bash
logFile=$1
while read line
do
for word in $line
do
# if word is ip address change to hostname
if [[ $word =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then
# check if ip address is correct
OIFS=$IFS
IFS="."
ip=($word)
IFS=$OIFS
if [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
then
echo -n `host $word | cut -d' ' -f 5`
echo -n " "
else
echo -n "$word"
echo -n " "
fi
# else print word
else
echo -n $word
echo -n " "
fi
done
# new line
echo
done < "$logFile"

Talking about IPv4: You may generate a list of sed-commands from your hosts file:
sed -rn 's/^(([0-9]{1,3}\.){3}([0-9]{1,3}))[ \t]([^ \t]+)[ \t].*/s#\1#\4#/p' /etc/hosts > hosts.sed
Then apply it on your logfile:
sed -f hosts.sed LOGFILE
Of course your hostsfilenames have to be listed in the hostfile.
Another, inverse approach would be to use logresolve.
From the manpage:
NAME
logresolve - Resolve IP-addresses to hostnames in Apache log files
SYNOPSIS
logresolve [ -s filename ] [ -c ] < access_log > access_log.new
SUMMARY
logresolve is a post-processing program to resolve IP-addresses in Apache's access logfiles. To minimize
impact on your nameserver, logresolve has its very own internal hash-table cache. This means that each
IP number will only be looked up the first time it is found in the log file.
Takes an Apache log file on standard input. The IP addresses must be the first thing on each line and
must be separated from the remainder of the line by a space.
So you could use REGEX's to extract all IPs, put them 2 times into a new file, once into the first column, and convert it with logresolve. Then use this table for generating such a sedfile as above.

The resolving can be done like this:
ip=72.30.38.140
hostname=nslookup $ip | grep name
hostname=${hostname#*name = }
hostname=${hostname%.}
This way IPs do not have to be in /etc/hosts.
The script itself depends on how your log looks like. Can you post an example?

This is the modified version of wisent's script I ended up using:
#!/bin/bash
logFile=$1
while read line
do
for word in $line
do
# if word is ip address change to hostname
if [[ $word =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,5}$ ]]
then
port=$(echo "$word" | sed -e "s/.*://")
word=$(echo "$word" | sed -e "s/:.*//")
OIFS=$IFS
IFS="."
ip=($word)
IFS=$OIFS
# check if ip address is correct and not 192.168.*
if [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && ${ip[2]} -le 255 && ${ip[3]} -le 255 && ${ip[0]}${ip[1]} -ne 192168 ]]
then
host=$(host $word | cut -d' ' -f 5)
if [[ $host =~ ^[0-9]{1,3}\(.*\)$ ]] # check for resolver errors
then
# if the resolver failed
echo -n "$word"
echo -n ":$port"
echo -n " "
else
# if the resolver worked
host=$(echo "$host'" | sed -e "s/\.'//" | sed ':a;N;$!ba;s/.*\n//g') # clean up cut's output
echo -n "$host"
echo -n ":$port"
echo -n " "
fi
else
# if the ip address isn't correct
echo -n "$word"
echo -n ":$port"
echo -n " "
fi
# else print word
else
echo -n $word
echo -n " "
fi
done
# new line
echo
done < "$logFile"

I added this to my .bashrc some time ago...
function resolve-hostname-from-ip()
{
if [ ! $1 ]
then
echo -e "${red}Please provide an ip address...${no_color}"
return 1
fi
echo "" | traceroute $1|grep " 1 "|cut -d ' ' -f4|cut -d '.' -f1
}
I have pre-defined terminal colors, so you can omit those if you like. =D
[root#somehostname ~ 08:50 AM] $ resolve-hostname-from-ip 111.22.33.444
someotherhostname
I have tested this on RHEL and SUSE successfully. I haven't tested it on IP's outside of my domain though, so I'm not 100% sure it will work in all cases...hope this helps =)

Related

Looping script, redirect to file

So I have this BASH script and what i want to do is, reach out to the servers. Check the used % of a directory. If it is higher than my set threshold (90) then print that server name to another file on the server where the script has been run from.
What it is doing is printing the first server name twice in to the file so it looks like
server1
server2
Here is my script ... I don't see why it would be going around in a loop to that first server twice
#!/bin/bash
SERVERS="server1
server2"
for i in $SERVERS; do
ssh $SERVERS "
df -h | grep var | awk '{print \$4}' | sed 's/%//g' > /home/user/space.txt
RESULTS=\$(grep -E "1[5-9]" /home/user/space.txt)
THRESHOLD=90
if [[ \$RESULTS -lt \$THRESHOLD ]]; then
exit 1;
elif [[ \$RESULTS -gt \$THRESHOLD ]]; then
hostname;
fi
" >> /home/user/problem.txt
done
Try this,
#!/bin/bash
SERVERS="server1
server2"
for i in $SERVERS; do
ssh "$i" "
df -h | grep var | awk '{print \$4}' | sed 's/%//g' > /home/user/space.txt
RESULTS=\$(grep -E "1[5-9]" /home/user/space.txt)
THRESHOLD=90
if [[ \$RESULTS -lt \$THRESHOLD ]]; then
exit 1;
elif [[ \$RESULTS -gt \$THRESHOLD ]]; then
hostname;
fi
" >> /home/user/problem.txt
done

updating a file using tee randomly fails in linux bash script

when using sed -e to update some parameters of a config file and pipe it to | tee (to write the updated content into the file), this randomly breaks and causes the file to be invalid (size 0).
In Summary, this code is used for updating parameters:
# based on the provided linenumber, add some comments, add the new value, delete old line
sed -e "$lineNr a # comments" -e "$lineNr a $newValue" -e "$lineNr d" $myFile | sudo tee $myFile
I set up an script which calls this update command 100 times.
In a Ubuntu VM (Parallels Desktop) on a shared Directory with OSX this
behaviour occurs up to 50 times
In a Ubuntu VM (Parallels Desktop) on the
Ubuntu partition this behaviour occurs up to 40 times
On a native System (IntelNUC with Ubuntu) this behaviour occurs up to 15 times
Can someone explain why this is happening?
Here is a fully functional script where you can run the experiment as well. (All necessary files are generated by the script, so you can simply copy/paste it into a bashscriptfile and run it)
#!/bin/bash
# main function at bottom
#====================
#===HELPER METHOD====
#====================
# This method updates parameters with a new value. The replacement is performed linewise.
doUpdateParameterInFile()
{
local valueOfInterest="$1"
local newValue="$2"
local filePath="$3"
# stores all matching linenumbers
local listOfLines=""
# stores the linenumber which is going to be replaced
local lineToReplace=""
# find value of interest in all non-commented lines and store related lineNumber
lineToReplace=$( grep -nr "^[^#]*$valueOfInterest" $filePath | sed -n 's/^\([0-9]*\)[:].*/\1/p' )
# Update parameters
# replace the matching line with the desired value
oldValue=$( sed -n "$lineToReplace p" $filePath )
sed -e "$lineToReplace a # $(date '+%Y-%m-%d %H:%M:%S'): replaced: $oldValue with: $newValue" -e "$lineToReplace a $newValue" -e "$lineToReplace d" $filePath | sudo tee $filePath >/dev/null
# Sanity check to make sure file did not get corrupted by updating parameters
if [[ ! -s $filePath ]] ; then
echo "[ERROR]: While updating file it turned invalid."
return 31
fi
}
#===============================
#=== Actual Update Function ====
#===============================
main_script()
{
echo -n "Update Parameter1 ..."
doUpdateParameterInFile "Parameter1" "Parameter1 YES" "config.txt"
if [[ "$?" == "0" ]] ; then echo "[ OK ]" ; else echo "[FAIL]"; return 33 ; fi
echo -n "Update Parameter2 ..."
doUpdateParameterInFile "Parameter2" "Parameter2=90" "config.txt"
if [[ "$?" == "0" ]] ; then echo "[ OK ]" ; else echo "[FAIL]"; return 34 ; fi
echo -n "Update Parameter3 ..."
doUpdateParameterInFile "Parameter3" "Parameter3 YES" "config.txt"
if [[ "$?" == "0" ]] ; then echo "[ OK ]" ; else echo "[FAIL]"; return 35 ; fi
}
#=================
#=== Main Loop ===
#=================
#generate file config.txt
printf "# Configfile with 3 Parameters\n#[Parameter1]\n#only takes YES or NO\nParameter1 NO \n\n#[Parameter2]\n#Parameter2 takes numbers\nParameter2 = 100 \n\n#[Parameter3]\n#Parameter3 takes YES or NO \nParameter3 YES\n" > config.txt
cp config.txt config.txt.bkup
# Start the experiment and let it run 100 times
cnt=0
failSum=0
while [[ $cnt != "100" ]] ; do
echo "==========run: $cnt; fails: $failSum======="
main_script
if [[ $? != "0" ]] ; then cp config.txt.bkup config.txt ; failSum=$(($failSum+1)) ; fi
cnt=$((cnt+1))
sleep 0.5
done
regards
DonPromillo
The problem is that you're using tee to overwrite $filepath at the same time as sed is trying to read from it. If tee truncates it first then sed gets an empty file and you end up with a 0 length file at the other end.
If you have GNU sed you can use the -i flag to have sed modify the file in place (other versions support -i but require an argument to it). If your sed doesn't support it you can have it write to a temp file and move it back to the original name like
tmpname=$(mktemp)
sed -e "$lineToReplace a # $(date '+%Y-%m-%d %H:%M:%S'): replaced: $oldValue with: $newValue" -e "$lineToReplace a $newValue" -e "$lineToReplace d" "$filePath" > "$tmpname"
sudo mv "$tmpname" "$filePath"
or if you want to preserve the original permissions you could do
sudo sh -c "cat '$tmpname' > '$filePath'"
rm "$tmpname"
or use your tee approach like
sudo tee "$filePath" >/dev/null <"$tmpname"
rm "$tmpname"

Bash getting everything to the same line?

I have been working on this all morning with no luck. I need the results to be all on one line comma delimited. Some portions were working fine but once I added the read it feel apart!
INPUT=steamIPs.csv
OLDIFS=$IFS
IFS=,
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
while read router contact address
do
myvar="$ip
$router
$contact
$address"
echo "$myvar" | paste -s -d','
OUTPUT=`snmpget $ip -c public -v 1 sysuptim`
echo ${OUTPUT#*:} | tr -d ' '
done < $INPUT
IFS=$OLDIFS
You're working too hard:
if [[ ! -f "$INPUT" ]]; then
echo "$INPUT file not found"
exit 99
fi
while IFS=, read -r router contact address; do
myvar="$ip,$router,$contact,$address"
echo "$myvar"
done < "$INPUT"

Checking if domain is active on server

I am trying to check if a domain is active on the server. So far I get errors.
list=/root/domainlist.txt
for i in $(cat $list)
do
echo "checking " $i
$ip = host $i |grep -o -m 100 '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
if [[ $ip == "xx.xx.xx.xx" ]]; then
$i >> /root/activedomains.txt
fi
done
Output:
activedomains: line 4: =: command not found
This is the current error I get.
No spaces before and after the =
No dollar sign in the assignment
You probably want the result of the command, so enclose it in $( )
ip=$(host $i |grep -o -m 100 '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
write to the file like this
echo "$i" >> /root/activedomains.txt
You have a syntax error with the line
$ip = host $i |grep -o -m 100 '...'
you shoud use instead :
ip=$(host $i |grep -o -m 100 '...')
A better way using boolean logic (no need grep there, if host $ip failed, it will return FALSE):
list=/root/domainlist.txt
while read ip; do
echo "checking $ip"
host "$ip" &>/dev/null && echo "$ip" >> /root/activedomains.txt
done < "$list"
It's the equivalent of
list=/root/domainlist.txt
while read ip; do
echo "checking $ip"
if host "$ip" &>/dev/null; then
echo "$ip" >> /root/activedomains.txt
fi
done < "$list"
For starters you shouldn't assign to $ip to ip ... but it's possible there are more errors.
My guess would be you wanted (line 4/5):
ip=$(host $i |grep -o -m 100 '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
Also read user000001's answer. The missing echo when getting the output is another issue.

Check for IP validity

How do I check the validity of an IP address in a shell script, that is within the range 0.0.0.0 to 255.255.255.255?
If you're using bash, you can do a simple regex match for the pattern, without validating the quads:
#!/usr/bin/env bash
ip=1.2.3.4
if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "success"
else
echo "fail"
fi
If you're stuck with a POSIX shell, then you can use expr to do basically the same thing, using BRE instead of ERE:
#!/bin/sh
ip=1.2.3.4
if expr "$ip" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
echo "success"
else
echo "fail"
fi
Note that expr assumes that your regex is anchored to the left-hand-side of the string, so the initial ^ is unnecessary.
If it's important to verify that each quad is less than 256, you'll obviously require more code:
#!/bin/sh
ip=${1:-1.2.3.4}
if expr "$ip" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
for i in 1 2 3 4; do
if [ $(echo "$ip" | cut -d. -f$i) -gt 255 ]; then
echo "fail ($ip)"
exit 1
fi
done
echo "success ($ip)"
exit 0
else
echo "fail ($ip)"
exit 1
fi
Or perhaps even with fewer pipes:
#!/bin/sh
ip=${1:-1.2.3.4}
if expr "$ip" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
IFS=.
set $ip
for quad in 1 2 3 4; do
if eval [ \$$quad -gt 255 ]; then
echo "fail ($ip)"
exit 1
fi
done
echo "success ($ip)"
exit 0
else
echo "fail ($ip)"
exit 1
fi
Or again, if your shell is bash, you could use a cumbersome regular expression for quad validation if you're not fond of arithmetic:
#!/usr/bin/env bash
ip=${1:-1.2.3.4}
re='^(0*(1?[0-9]{1,2}|2([0-4][0-9]|5[0-5]))\.){3}'
re+='0*(1?[0-9]{1,2}|2([‌​0-4][0-9]|5[0-5]))$'
if [[ $ip =~ $re ]]; then
echo "success"
else
echo "fail"
fi
This could also be expressed in BRE, but that's more typing than I have in my fingers.
And lastly, if you like the idea of putting this functionality ... in a function:
#!/usr/bin/env bash
ip=${1:-1.2.3.4}
ipvalid() {
# Set up local variables
local ip=${1:-NO_IP_PROVIDED}
local IFS=.; local -a a=($ip)
# Start with a regex format test
[[ $ip =~ ^[0-9]+(\.[0-9]+){3}$ ]] || return 1
# Test values of quads
local quad
for quad in {0..3}; do
[[ "${a[$quad]}" -gt 255 ]] && return 1
done
return 0
}
if ipvalid "$ip"; then
echo "success ($ip)"
exit 0
else
echo "fail ($ip)"
exit 1
fi
There are many ways you could do this. I've shown you just a few.
This single regex should validate only those addresses between 0.0.0.0 and 255.255.255.255:
#!/bin/bash
ip="1.2.3.4"
if [[ "$ip" =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$ ]]; then
echo "success"
else
echo "fail"
fi
Use ipcalc ( tested with the version package in RPM initscripts-9.49.49-1)
$ ipcalc -cs 10.10.10.257 && echo vaild_ip || echo invalid_ip
invalid_ip
The script Validating an IP Address in a Bash Script
by Mitch Frazier does what you want to do:
function valid_ip()
{
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return $stat
}
The typical solutions for this all seem to use regular expressions, but it occurs to me that it might be a better approach to do something like:
if echo "$ip" | { IFS=. read a b c d e;
test "$a" -ge 0 && test "$a" -le 255 &&
test "$b" -ge 0 && test "$b" -le 255 &&
test "$c" -ge 0 && test "$c" -le 255 &&
test "$d" -ge 0 && test "$d" -le 255 &&
test -z "$e"; }; then echo is valid; fi
i tweaked all the codes and found this to be helpful.
#!/bin/bash
ip="256.10.10.100"
if [[ "$ip" =~ (([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5]))$ ]]; then
echo "success"
else
echo "fail"
fi
I prefer to use ipcalc to do this, as long as my script doesn't have to be portable.
ipcalc 1.1.1.355
INVALID ADDRESS: 1.1.1.355
Address: 192.168.1.1 11000000.10101000.00000001. 00000001
Netmask: 255.255.255.0 = 24 11111111.11111111.11111111. 00000000
Wildcard: 0.0.0.255 00000000.00000000.00000000. 11111111
=>
Network: 192.168.1.0/24 11000000.10101000.00000001. 00000000
HostMin: 192.168.1.1 11000000.10101000.00000001. 00000001
HostMax: 192.168.1.254 11000000.10101000.00000001. 11111110
Broadcast: 192.168.1.255 11000000.10101000.00000001. 11111111
Hosts/Net: 254 Class C, Private Internet
There is a great page showing how to use it in scripting, etc, here:
SleeplessBeastie's Notes
If someone still looking for an answer just by using regex, below would work -
echo "<sample ip address>"|egrep "(^[0-2][0-5]{1,2}?\.|^[3-9][0-9]?\.)([0-2][0-5]{1,2}?\.|[3-9][0-9]?\.)([0-2][0-5]{1,2}?\.|[3-9][0-9]?\.)([0-2][0-5]{1,2}?$|[3-9][0-9]?$)"
Perl has a great module Regexp::Common for validating various things:
perl -MRegexp::Common=net -e 'exit(shift() !~ /^$RE{net}{IPv4}$/)' $ipaddr
You may need to sudo cpan install Regexp::Common first
I'd wrap it in a function:
valid_ip() {
perl -MRegexp::Common=net -e 'exit(shift() !~ /^$RE{net}{IPv4}$/)' "$1"
}
if valid_ip 123.234.345.456; then
echo OK
else
echo INVALID
fi
Alternate version that still does a thorough validation (meaning that it requires both a properly formatted IP address AND that each quadrant is within the range of allowed values aka 0-255). Works fine on GNU bash 4.4.20 (Linux Mint 19.3); no promises elsewhere but will prolly be fine as long as you have bash 4.
The initial format check regex is borrowed from the shannonman / Mitch Frazier answer above; the rest is my own.
function isValidIpAddr() {
# return code only version
local ipaddr="$1";
[[ ! $ipaddr =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && return 1;
for quad in $(echo "${ipaddr//./ }"); do
(( $quad >= 0 && $quad <= 255 )) && continue;
return 1;
done
}
function validateIpAddr() {
# return code + output version
local ipaddr="$1";
local errmsg="ERROR: $1 is not a valid IP address";
[[ ! $ipaddr =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && echo "$errmsg" && return 1;
for quad in $(echo "${ipaddr//./ }"); do
(( $quad >= 0 && $quad <= 255 )) && continue;
echo "$errmsg";
return 1;
done
echo "SUCCESS: $1 is a valid IP address";
}
$ isValidIpAddr '192.168.0.1'
$ echo "$?"
0
$ isValidIpAddr '192.168.0.256'
$ echo "$?"
1
$ validateIpAddr '12.1.10.191'
SUCCESS: 12.1.10.191 is a valid IP address
$ validateIpAddr '1.1.1.127'
SUCCESS: 1.1.1.127 is a valid IP address
$ validateIpAddr '1.1.1.1337'
ERROR: 1.1.1.1337 is not a valid IP address
We can use "ip route save" to do the check.
valid_addrmask()
{
ip -4 route save match $1 > /dev/null 2>&1
}
$ valid_addrmask 255.255.255.255 && echo "is valid" || echo "is not valid"
is valid
$ valid_addrmask 255.255.255.355 && echo "is valid" || echo "is not valid"
is not valid
#!/bin/bash
read -p " ip: " req_ipadr
#
ip_full=$(echo $req_ipadr | sed -n 's/^\(\(\([1-9][0-9]\?\|[1][0-9]\{0,2\}\|[2][0-4][0-9]\|[2][5][0-4]\)\.\)\{3\}\([1-9][0-9]\?\|[1][0-9]\{0,2\}\|[2][0-4][0-9]\|[2][5][0-4]\)\)$/\1/p')
#
[ "$ip_full" != "" ] && echo "$req_ipadr vaild ip" || echo "$req_ipadr invaild ip"
You can just copy the following code and change body of if else control as per your need
function checkIP(){
echo "Checking IP Integrity"
ip=$1
byte1=`echo "$ip"|xargs|cut -d "." -f1`
byte2=`echo "$ip"|xargs|cut -d "." -f2`
byte3=`echo "$ip"|xargs|cut -d "." -f3`
byte4=`echo "$ip"|xargs|cut -d "." -f4`
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ && $byte1 -ge 0 && $byte1 -le 255 && $byte2 -ge 0 && $byte2 -le 255 && $byte3 -ge 0 && $byte3 -le 255 && $byte4 -ge 0 && $byte4 -le 255 ]]
then
echo "IP is correct"
else
echo "This Doesn't look like a valid IP Address : $ip"
fi
}
checkIP $myIP
Calling the method with IP Address stored in a variable named myIP.
$ip =~ ^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ - This part makes sure that IP consists of 4 blocks separated by a dot(.) but every block here is allowed to range from 0 - 999
Since desired range of every block would be 0 - 255, to make sure of that below line can be used.
$byte1 -ge 0 && $byte1 -le 255 && $byte2 -ge 0 && $byte2 -le 255 && $byte3 -ge 0 && $byte3 -le 255 && $byte4 -ge 0 && $byte4 -le 255
In the most simple form:-
#!/bin/bash
while true;
do
read -p "Enter a ip: " IP
echo "${IP}" > ip.txt
OCT1=$(cat ip.txt | awk -F "." '{print $1}')
OCT2=$(cat ip.txt | awk -F "." '{print $2}')
OCT3=$(cat ip.txt | awk -F "." '{print $3}')
OCT4=$(cat ip.txt | awk -F "." '{print $4}')
REGEX_IP='^[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}$'
if [[ ${IP} =~ ${REGEX_IP} ]]
then
if [[ ${OCT1} -gt 255 || ${OCT2} -gt 255 || ${OCT3} -gt 255 || ${OCT4} -gt 255 ]]
then
echo "Please enter a valid ip"
continue
fi
break
else
echo "Please enter a valid ip"
continue
fi
done
This will cover all the scenarios.
May be it is usefull
#this script verify either a ip address is valid or not as well as public or local ip
#$1 means supplied first argument
ip=$(echo $1 | gawk '/^[0-9]{1,3}\.[0-9]{1,3}+\.[0-9]{1,3}+\.[0-9]{1,3}$/{print $0}')
#regular expression to match pattarn from 0.0.0.0 to 999.999.999.999 address
ip1=$(echo $ip | gawk -F. '{print $1}')
ip2=$(echo $ip | gawk -F. '{print $2}')
ip3=$(echo $ip | gawk -F. '{print $3}')
ip4=$(echo $ip | gawk -F. '{print $4}')
echo "Your ip is : $ip1.$ip2.$ip3.$ip4" #extract four number from the address
#To rectify original ip range 0-255
if [[ $ip1 -le 255 && $ip1 -ne 0 && $ip2 -ne 0 && $ip2 -le 255 && $ip3 -ne 0 && $ip3 -le 255 && $ip4 -ne 0 && $ip4 -le 255 ]]
then
echo "This is a valid ip address"
else
echo "This is not a valid ip address"
fi
if [[ $ip1 -eq 198 ]]
then
echo "It may be a local ip address"
else
echo "It may be a public ip address"
fi
#!/bin/bash
IP="172.200.22.33.88"
p=`echo $IP | tr '.' '\n' | wc -l`
echo $p
IFS=.
set $IP
echo $IP
a=$1
b=$2
c=$3
d=$4
if [[ $p == 4 && $a -lt 255 && $b -lt 255 && $c -lt 255 && $d -lt 255 ]]
then
echo " THIS is Valid IP "
else
echo "THIS IS NOT VALID IP ADDRESS"
fi
Validating IPv4 if is local
valid_ip(){
local ip=$IP
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
if [[ "$stat" = "0" ]];
then
echo "IPv4 Valid"
if [[ "${ip[0]}" = 192 || "${ip[0]}" = 10 || "${ip[0]}" = 172 ]];
then
echo "IPv4 is local"
stat=1
fi
else
echo "IPv4 not valid"
fi
return $stat
}
IP=10.10.10.1
valid_ip
Check out my solution if you like it. Simple, readable, no extra variables.
function valid_ip () {
[[ ${1} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] \
|| return 1
for i in ${1//./ }; do
[[ ${i} -le 255 ]] \
|| return 1
done
}
Usage:
ips='192.168.1.1 192.168.1.333
8.8.8.8 8.8.8 a.b.c.d blabla'
for ip in ${ips}; do
valid_ip "${ip}" \
&& echo "${ip} is valid" \
|| echo "${ip} is INVALID"
done
Output:
192.168.1.1 is valid
192.168.1.333 is INVALID
8.8.8.8 is valid
8.8.8 is INVALID
a.b.c.d is INVALID
blabla is INVALID
I use the following on my router, running the Ash shell. This scripts has a very small footprint, as it only uses builtin commands, and no forking or subshells. It implements a checkIP() function, that returns false if the IP is invalid, and true if valid.
#
# basic validation on the IPv4 address
checkIPv4()
{
local IP="$1"
local N
local OIFS
# only numbers and dots in the entire IP address, no empty quads, and no
# leading or trailing dots
case "${IP}" in
*[!0-9.]* | *..* | .* | *. ) #
return 1
;;
esac
OIFS="${IFS}"
IFS=.
set -- $IP
IFS="${OIFS}"
if [ $# -ne 4 ]; then
return 1
fi
for N in "$#"; do
if [ "${#N}" -lt 1 -o "${#N}" -gt 3 ]; then
return 1
fi
# at this point, we are guaranteed it is a positive number
# of reasonable length
if [ "$N" -gt 255 ]; then
return 1
fi
done
return 0
}
I like the answer posted by Neo.
For clarity, I would add a variable for the duplicate portion of the regex.
#!/bin/bash
ip="1.2.3.4"
regex0to255='([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))'
if [[ "${ip}" =~ ^(${regex0to255}\.){3}${regex0to255}$ ]]; then
echo "success"
else
echo "fail"
fi
How about this?
# ip route get 10.10.10.100 > /dev/null 2>&1 ; echo $?
0
# ip route get 10.10.10.300 > /dev/null 2>&1 ; echo $?
1
Since the "ip" command checks the validity of IP in itself.
(2022/9/17) When the IP is not reachable i.e. network interface is down,
$ ip route get 10.10.10.100 > /dev/null 2>&1 ; echo $?
2
$ ip route get 10.10.10.300 > /dev/null 2>&1 ; echo $?
1
This means one can still distinguish if the IP is valid or not.
However, a better solution would be to write a small program, for example using inet_pton.
My comment in another thread,
https://unix.stackexchange.com/a/581081/65646

Resources