Unable to get uniq -c in while loop - linux

I'm trying to sort a log file and get a total count of unique IP addresses and do something with the ones that appear > than n. this is my first command:
$ grep -B 1 "foobar" ip.log | grep "IP Address" > ip_count.log
which outputs:
IP Address : 133.55.39.56
IP Address : 116.243.70.151
IP Address : 117.46.13.194
IP Address : 115.179.82.10
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 13.123.2.123
IP Address : 33.123.2.123
IP Address : 33.123.2.123
IP Address : 33.123.2.123
IP Address : 33.123.2.123
IP Address : 33.123.2.123
IP Address : 33.123.2.123
if i do this I get a proper count:
$ awk '{print $4}' ip_count.log | uniq -c
1 133.55.39.56
1 116.243.70.151
1 117.46.13.194
1 115.179.82.10
9 13.123.2.123
6 33.123.2.123
but if I do this I don't:
$ while read -r line ; do c=$(echo $line | awk '{print $4}' | \
uniq -c | awk '{print $1}') ; if [[ $c -gt 1 ]]; then echo "$line" ; \
fi ; done < ip_count.log
1 133.55.39.56
1 116.243.70.151
1 117.46.13.194
1 115.179.82.10
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 13.123.2.123
1 33.123.2.123
1 33.123.2.123
1 33.123.2.123
1 33.123.2.123
1 33.123.2.123
1 33.123.2.123
i'm not sure what i'm doing wrong within the loop, so maybe someone could tell me. there's probably some better way to combine all this into one command anyway, so any tips would be appreciated, thanks.

It seems no loop is needed :
uniq -c ip_count.log | awk '$1 > 1 { print $1" "$5 }'

When you read the file in the loop, it executes command row by row.
awk '{print $4}' ip_count.log | uniq -c
In the above, all records are read from stdin and process.
when you execute loop while read -r line your variable $line only contains 1 record instead of the whole file.
You can do it in single awk command using an associative array.
An alternative solution using awk
$awk -F':' '{ count=1; a[$2]=a[$2]+count} END {for (i in a) if (a[i]>1 )print i, a[i]} ' ip_count.log
33.123.2.123 6
13.123.2.123 9
$
Explanation:
-F':' --> we are setting custom field seprator as :
count=1; --> Initialize a variable as default value 2
a[$2]=a[$2]+count --> Create a associative array with $2 as index and count as values.
END --> Wait till { } code block execution is complete
{for (i in a) if (a[i]>1 )print i, a[i]} --> Loop through each value in array
Also, it's a good practice to pass sorted input to uniq command.
For example:
$echo -e "a\na\nb\na"
a
a
b
a
$echo -e "a\na\nb\na" | uniq -c
2 a
1 b
1 a
$echo -e "a\na\nb\na" | sort | uniq -c
3 a
1 b
$

Related

Printing with multiple delimiters

Im using this script
#!/bin/bash
echo Su direccion IP es:
/sbin/ifconfig | grep 'inet addr:' | cut -d: -f2 | awk '{print $1}'
which outputs a list of all of my ip addresses
but id like to add before each address the corresponding name
example
eth0: adresss1
tun0: address2
any advice? id be grateful for some explanation
i would also like to filter the lo connection if possible? thanks
Another awk:
ifconfig | awk -v OFS=": " -v RS= '$1!="lo" && split($0, a, /inet addr:/) > 1{
sub(/ .*/, "", a[2]); print $1, a[2]}'
eth0: 192.168.0.101
tun0: 10.1.111.123
ip -o addr list | awk -F'[[:space:]/]+' '$3 == "inet" && $2 != "lo" { print $2 ": " $4 }'
ip r show | awk '{print $3 $1}'
I think my take on it would be this:
$ ip addr |
awk '/^[0-9]+:/{print ""} 1' |
awk -vRS= '{for(i=1;i<NF;i++)if($i~/^inet/)print $2,$(i+1)}'
This uses two awk instances: the first splits records by adding blank lines, and the second to step through fields searching for addresses.
The output of this pipeline will contain one line per IP address (IPv6 included).
Note that in ifconfig notation, a second IP address on an interface might show up as a "sub-interface" like eth0:0. This notation is only useful in conjunction with ifconfig, which as you've seen in comments is not the recommended way to deal with modern linuces.

script for checking IP's hostname from file

I used for to file one contain the IP with hostname like the /ec/hosts
and the other contain the IP and counter try to print the ip if hostname not found and if is found print the hostname.
Script:
for i in `cat ip | awk '{print $2}'` ;do
var=`grep "$i" Server_ip` |
awk ' {if($var == "") print $i else print $1}';
done
File 1
localhost 127.0.0.1
test 10.0.0.1
test1 10.0.0.2
File 2
3 127.0.0.1
2 10.0.0.1
1 10.0.0.2
4 10.0.0.3
5 10.0.0.4
Desired output
localhost
test
test1
10.0.0.3
10.0.0.4
You can try this;
#!/bin/bash
for i in `cat ip | awk '{print $2}'` ;do
var=`awk -v ip=${i} '$2 == ip {print $1}' Server_ip`
if [ -z "$var" ]; then
echo $i
else
echo "$var"
fi
done
Eg;
user#host:/tmp/test$ cat Server_ip
localhost 127.0.0.1
test 10.0.0.1
test1 10.0.0.2
user#host:/tmp/test$ cat ip
3 127.0.0.1
2 10.0.0.1
1 10.0.0.2
4 10.0.0.3
5 10.0.0.4
user#host:/tmp/test$ ./test.sh
localhost
test
test1
10.0.0.3
10.0.0.4

Table formatting shell script

I have a shell script which extracts information from Vservers, this is the script:
for i in {130..136}; do
> ./vserver/Info$i
ssh 132.138.180.$i "hostname;
echo 'Virtual'
echo ''
cat /etc/issue | head -1
echo ''
dmidecode | grep Socket | tail -1 | awk '{print \$4}'
echo ''
free -g | grep Mem | awk '{ print \$2 }'
echo ''
fdisk -l | grep Disk | wc -l
echo ''
df -h | grep ^/ | wc -l
echo ''
ifconfig | grep inet | awk '{print \$2 }' | cut -c 6- | awk '\$1=\$1' ORS=' '
echo ''
ifconfig | grep -b1 inet | grep HWaddr | awk '{ print \$5 }' | awk '\$1=\$1' ORS=' '
echo ''
ip route show | grep default | awk '{ print \$3 }' | awk '\$1=\$1' ORS=' '
echo ''
cat /etc/resolv.conf | grep name | awk '{ print \$2 }' | awk '\$1=\$1' ORS=' '
echo ''
mount | grep el01 | awk '{ print \$1 \" -> \" \$3 }' | awk '\$1=\$1' ORS=' '
echo ''
netstat -nr | awk '{ print \$1, \$2, \$3, \$8 }'
echo ''
" >> ./vserver/Info$i
done
I have the following output(example):
el01test
Virtual
Oracle Linux Server
2
7
1
2
19.16.10.111 12.1.0.1 12.1.0.11 12.1.2.11 127.0.0.1
00:22:4F:F9:3C:D8 80:22:05:F7:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00 22:44:22:F4:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00 22:44:22:E2:FE:80:00:00:00:00:00:00:00:00:00:00:00:00:00:00
19.16.10.1
19.16.10.12 19.16.10.15
el01:/export/el011->/home/glassfish/glassfish el01:/export/logs/el01vur01->/home/glassfish/logs el01:/export/home/oem12ag/age->/home/oem12ag/agent
Kernel IP routing
Destination Gateway Genmask Iface
0.0.0.0 192.168.181.1 0.0.0.0 bond0
169.254.0.0 0.0.0.0 255.255.0.0 bond1
17.1.0.0 0.0.0.0 255.255.0.0 bond2
17.1.0.0 0.0.0.0 255.255.0.0 bond1
19.16.0.0 0.0.0.0 255.255.252.0 bond3
19.16.10.0 0.0.0.0 255.255.252.0 bond0
I would like to format my info like this:
hostname OS Core_number Free_memory IP1
IP2
IP3
I've been trying by using awk but I haven't had much luck with it. Thanks for your help!
You can do it with awk, by reading in the lines into an array. Then output the lines containing the information into the required table format.
Here is an example to give you the rough idea:
script.awk
{ info[ i++ ] = $1 }
END { printf("%s\t%s\t%s\t%s\t%s\n", info[0], info[3], info[5], info[7], info[9])
printf("\t\t\t\t\t\t\t%s\n", info[10])
printf("\t\t\t\t\t\t\t%s\n", info[11])
}
Use it like this: awk -f script.awk yourfile.
i finally finish my formatting script, thanks to all of you for your help, finally y had to modify the initial script and then format it using the next code:
for q in $(ls); do awk ' NR >= 19 { route[ j++ ] = $0; next } NF==1 { info[ i++ ] = $1; next } NR == 4 { OS= $0 } NR == 14 { A=split($0, Ip, " "); next } NR == 15 { B=split($0, Mac, " "); next } NR == 17 { C=split($0, Dns, " "); next } NR == 18 { D=split($0, Mount, " "); next } END { if (A > B) max1=A ; else max1=B; if (C > D) max2=C; else max2=D; if (max1 > max2) max=max1; else max=max2; if (max < j) max=j; for (i=0; i<=6; i++) {printf "%s\t ",info[i]} ; for (w = 0; w <= max; w = w+1) { printf("\t\t\t\t\t\t\t%s\t%s\t%s\t%s\t%s\n",Ip[w], Mac[w], Dns[w], Mount[w], route[w]) } } ' $q; done > Fullout
Thanks a lot for your help!
If maybe someone knows how to mix the printf in order to them be able to print at the same line i would appreciated.
I'm also aware that the code could be optimised but i was in a hurry, sorry :(.
I posted one of the output of the initial script in the initial answer if anyone wants to test it for your further scripting.
Thanks
Instead of writing multiple tabs, you could set tabstops using tabs command, or ANSI code inline.
ANSI/VT100 Terminal Control Escape Sequences
or
Linux tabs command
Then, write your output code with a single \t tab.

How to find out your IP addresses using bash

I have a server that has several ip addresses. I want to work out their exact values in bash. I am looking for something like:
a=returnIpAddressStartingWith 10.60.12
b=returnIpAddressStartingWith 10.60.13
so that the following returns:
> echo $a
10.60.12.23
Is there a reasonable way of doing this on linux?
You can use a function like this for searching:
findip() {
ip -4 addr | awk -v ip="$1" -F '[/[:blank:]]+' '$2 == "inet" && index($3, ip){print $3}'
}
And find the IP by:
a=$(findip '10.60.12')
Parse it out of the 'ip addr show' list using grep/awk/cut, then optionally, if you need to access it as an array, copy your list into a Bash array:
# Create a string that is the list of all variables
IPSTR=`ip addr show | fgrep 'inet ' | fgrep -v '127.0.0.1' | awk '{ print $2 }' | cut -d '/' -f 1`
I=0
for IP in $IPSTR ; do
IPARY[$I]=$IP
I=$(($I+1))
done
echo "First IP in array is ${IPARY[0]}"
echo "Number of IP addresses in array is ${#IPARY[*]}"

Show active ip-addresses in linux script with % or UP/DOWN

I have an ip-address: 192.168.0.205
I can ping it in a script:
for i in 192.168.0.205 ....
do
ping -c 1 $i
done
but now I want the script shows:
192.168.0.205 UP or DOWN
Is there also a way to show it in % e.g: 75% UP?
What you may want is this:
x=0;
a=1
b=5
for((i=$a;i<=$b;i++))
do
y=`ping -n 1 192.168.1.$i | grep unreachable | wc -l`
x=$(($x + $y))
done;
echo "$x $b $a" | awk '{printf "%.2f %% DOWN\n",($1/($2-$3+1))*100}';
x: contains the number of DOWN IPs
a: last octet of first IP address
b: last octet of last IP address
y: contains 0 if an IP is UP else 1
I hope that helps!
PS: Change -n to -c or whatever option does the work in your shell!

Resources