Is there any linux command/program that can tell me subnet range if i provide the number of ip's? - linux

Is there any linux command or script that I can invoke to get ip range with subnet, if I pass number of ip's as an argument, like if I say 256 then it should return me 10.0.0.0/24, Lets say I am talking about 10.0.0.0 range as of now.

If you have Perl installed, you can calculate that value with the following script:
perl -le 'print 32-int(log(<>)/log(2));'
This takes input from standard in, so you can pipe your desired number of IPs as follows:
echo 256 | perl -le 'print 32-int(log(<>)/log(2));'
# prints 24 followed by a newline to stdout
If you want it to display an IP before, you can run this Perl program:
echo 256 | perl -le 'print "10.0.0.0/" . (32-int(log(<>)/log(2)));'
# prints 10.0.0.0/24 followed by a newline to stdout
The subnet mask can be calculated by subtracting the log base 2 of the desired number of IPs from 32 (IPv4 netmask length), and that's what the above Perl script does.

Related

How read line by line in bash and assign the value to a variable [duplicate]

This question already has answers here:
Read a file line by line assigning the value to a variable [duplicate]
(10 answers)
Closed 2 years ago.
I have a file called ips.txt and it has values like below:
cat ips.txt
abc.com. 10.120.20.4 10.120.20.5 ... # there can be many ips separated by a space
xyz.com. 10.120.20.6
I want to read this file line by line with a loop and do some other works.
And als I need to remove that . as well at the end of each domain name abc.com. and assign each ip to a variable (if it can be stored to an array, it is great)
So here's what I tried:
input="ips.txt"
while IFS= read -r line
do
echo "$line"
domain="" # need to assign abc.com
ip_one= "" # need to assign the first ip occurence
ip_two= "" # need to assign the second ip occurence
...
ip_n= "" # need to assign the nth ip occurence
# some other commands I need to execute with domain name and all the ips collected
done < "$input"
how can I assign the ip values to different variables? and use them? It is better if I can Store the IP's in a array like data type so it is more easier as I don't know how many IP's are there for some line in the ips.txt file.
Can someone help me do this?
Using set, and shift:
while IFS= read -r line; do
set $line
domain=$(sed 's/\.$//' <<< "$1")
shift
for ip in $#; do
echo "domain: $domain ip: $ip"
done
done < "$input"
Start with a standard array read. Assign to $#, use parameter expansion to strip the dot, then shift it off the stack, and you can assign back to your original array.
input="ips.txt"
declare -A lookup=()
while read -a ips # ips is an array
do set -- "${ips[#]}" # assign the array to $#
domain="${1%.}" # assign abc.com without the dot
shift # dump the first column; now $# is just the IP's
for ip in "$#"; do lookup[$ip]="$domain"; done # assign the domain to each IP
done < "$input"
now
$: echo ${lookup[10.120.20.5]}
abc.com

Characters in string getting replaced when echoed

I am writing a simple script to collect 2 IP addresses. I am using the open stack client to gather the allocation pool of a provider network. I used awk to gather the 2 IP addresses (start and end) and put them into 2 variables. When I echo the 2 variables alone they print out how I expect. However, if I try to echo something after the variable, it seems to replace the first few characters of the IP address.
It hard to explain, but if you refer to the output it should make more sense. If you look at my script below, I just put the string "hello" after the variable in each echo statement for demonstrative purposes.
#!/bin/bash
NETWORK=$1
#just gets the allocation pool IP addresses from openstack
ALLOCATION_POOLS=$(openstack subnet show $NETWORK --insecure|grep -w "allocation_pools"|awk -F " " '{print $4}')
POOL_START=$(awk -F "-" '{print $1}' <<< "$ALLOCATION_POOLS")
echo $POOL_START"hello"
POOL_END=$(awk -F "-" '{print $2}' <<< "$ALLOCATION_POOLS")
echo $POOL_END"hello"
Here is the output:
hello.146.87
hello.146.126
If I did not put "hello" in the echo statement, the output looks more like this:
10.28.146.87
10.28.146.126
Another thing I did was tested the length of the strings, and the length was larger then the number of characters in the ip address. I believe that there is some strange character after the IP addresses that is causing this. If that is the case, how can I remove it?

checking information of different computers in a network using bash script

I am trying to write a bash script to access a file (nodeNames) that contains ip addresses to different computers in a cluster network, ssh into each of these computers and output some basic information namely: Hostname, Host IPaddress, load average and the process using the most memory and append all these information into a file seperating each wit commas. Also, each of the computers have same user and password. This is my code so far but it isn't working, please I need help here
egrep -ve '^#|^$'nodeNames | while read a
do
ssh $a "$#" &
output1=`hostname`
#This will display the server's IP address
output2=`hostname -i`
#This will output the server's load average
output3=`uptime | grep -oP '(?<=average:).*'| tr -d ','`
#This outputs memory Information
output4=`ps aux --sort=-%mem | awk 'NR<=1{print $0}'`
#This concantenates all output to a single line of text written to
echo "$output1, $output2, $output3, $output4" | tee clusterNodeInfo
done
You need to understand what is executed on which computer. The shell script you start is executed on your host A and you want information from your host B. ssh $a "$#" & will not suddenly make all the commands execute on the remote host B. Therefore, the
output1=`hostname`
will be executed on host A and output1 will have the hostname of host A.
You may also want to put the tee outside the loop or use tee -a to prevent overwriting your output file.
For bash, use $() instead of `` .
So, that would make your script:
egrep -ve '^#|^$'nodeNames | while read a
do
output1=$(ssh $a hostname)
#This will display the server's IP address
output2=$(ssh $a hostname -i)
#This will output the server's load average
output3=$(ssh $a "uptime | grep -oP '(?<=average:).*'| tr -d ','")
#This outputs memory Information
output4=$(ssh $a "ps aux --sort=-%mem | awk 'NR<=1{print $0}'")
#This concantenates all output to a single line of text written to
echo "$output1, $output2, $output3, $output4" | tee -a clusterNodeInfo
done
(have not tested it, but it should be something like this)

Bash script is printing the wrong value

I've created a script that seperates the IP that it finds, based on NIC card name input.
#!/bin/bash
echo what is your NIC?
read NIC
IP=`ifconfig $NIC 2>/dev/null|awk '/inet addr:/ {print $2}'|sed 's/addr://'`
NEWSTRING=${IP:0:6}
ALPHARETTA="12.101"
EUFAULA="12.102"
if [ "${NEWSTRING}" = "${ALPHARETTA}" ] ; then
echo I'm in Alpharetta
else
echo I'm in Eufaula
fi
If eth0 were to be 12.101.1.1 it would only take (12.101)
I'm comparing 12.101 and 12.101 for my tests... and i'm getting this echo back....
what is your NIC?
eth0
Im in Alpharetta
else
echo Im in Eufaula
I'm obviously doing something silly, and not seeing it.. could somebody point me in the correct direction?
The bash parser is seeing the apostrophe characters you are trying to echo in the word "I'm" and it thinks you are trying to print one long string that spans from line 10 to line 12 of your script. You can even see how the syntax highlighting on this site is also indicating a problem. You should wrap the message you are echoing in quotes. For example:
echo "I'm in Alpharetta"

Is there any better way to get mac address from arp table?

I want to get a mac address from arp table by using ip address. Currently I am using this command
arp -a $ipAddress | awk '{print $4}'
This command prints what I want. But I am not comfortable with it and I wonder if there is any built-in way or more stable way to do this.
You can parse the /proc/net/arp file using awk:
awk "/^${ipAddress//./\.}\>/"' { print $4 }' /proc/net/arp
but I'm not sure it's simpler (it saves one fork and a subshell, though).
If you want a 100% bash solution:
while read ip _ _ mac _; do
[[ "$ip" == "$ipAddress" ]] && break
done < /proc/net/arp
echo "$mac"
Well, you could write a program (such as in C) to actually use the ARP protocol (yes, I know that's redundant, like ATM machine or PIN number) itself to get you the information but that's likely to be a lot harder than a simple pipeline.
Perhaps you should examine your comfort level a little more critically, since it's likely to cause you some unnecessary effort :-)
The manpage for the Linux ARP kernel module lists several methods for manipulating or reading the ARP tabes, ioctl probably being the easiest.
The output of arp -a is locale dependent (i.e. it changes with your system language). So it might be a good idea to at least force it to the default locale:
LC_ALL=C arp -a $ipAddress | awk '{print $4}'
However, I share your fear that the output of arp -a is not meant to be parsed. If your program is restricted to linux system, another option would be to parse the file /proc/net/arp. This file is exported by the kernel and is what arp itself parses to get its information. The format of this file is described in the manpage proc(5), see man 5 proc.
This can be easily done with awk:
awk '$1==IPADDRESS {print $4}' /proc/net/arp
Here's an awk + sed solution which doesn't assume the column number is always 4.
#!/bin/bash
cat /proc/net/arp |\
# remove space from column headers
sed 's/\([^ ]\)[ ]\([^ ]\)/\1_\2/g' |\
# find HW_address column number and/or print that column
awk '{
if ( !column ) {
for (i = 1; i <= NF; i++ ) {
if ( $i ~ /HW_address/ ) { column=i }
};
print $column
}
else {
print $column
}
}'
There are still fragile assumptions here, such as the column name being "HW address".
Update, removed PIPE
sed -nr 's/^'${ipAddress//./\.}'.*(([0-9A-Za-z]{2}:){5}[0-9A-Za-z]{2}).*$/\1/p' /proc/net/arp
Solution for non-fixed column;
arp -a $ipAddress | sed -n 's/^.*\(\([0-9A-Z]\{2\}:\)\{5\}[0-9A-Z]\{2\}\).*$/\1/p'
Explanation
^.* - Match start of string ^ followed by any character .*.
[0-9A-Z]\{2\}: - Match any character of numeric alpha-numeric twice followed by colon.
\([0-9A-Z]\{2\}:\)\{5\} - Match the pattern between the ( ) five times.
[0-9A-Z]\{2\} - Match any character of numeric alpha-numeric twice.
.*$ - Match any characters zero or more times .* until end of string $.
\1/p - Return capture pattern 1 / p print the match.
You can use this one for scripting:
awk ' $1~/[[:digit:]]/ {print $4}' /proc/net/arp
what it do:
read /proc/net/arp (standard arp output)
searchig for strings with [0-9]
get the 4rd "column" with mac adresses
Enjoy!
I prefer to use the arping command to explicitly query the MAC of some IP address (this also updates the local ARP cache):
arping -c 1 192.168.2.24 | grep -Eo "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]"
It's very useful to find if there exist two or more hosts using the same IP address (add -D option), or to check the current IP addresses used in the local VLAN with a simple script like:
for i in $(seq 1 254); do
IP="192.168.5.$i"
MAC=$(arping -c 1 $IP | grep -Eo "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]")
if [ "$MAC" ] ; then
echo "$IP $MAC"
fi
done
Note that arping can't detect the IP address of the local host in this way (but we can add checks in the script to show it if exists in the range).
There exist several versions of arping with slightly different options and output. In Linux Ubuntu there are one in the package iputils-arping and other in the package arping.
Note: To answer the question and not the problem, when filtering /proc/net/arp you must use a regex that ensures the full match, like ending the expression with a space (otherwise, in this example, it will show also 2.240-2.249 addresses if present):
ipaddress="192.168.2.24"
grep "^${ipaddress} " /proc/net/arp | grep -Eo "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]")

Resources