Fetch value after certain pattern in Unix - shell

This is the text that I have
Peer Addr 1.1.1.1, Intf Port-Channel0.1, State Up
VRF default, LAddr 1.1.1.1, LD/RD 090/592
Session state is Up and using echo function with 300 ms interval
Detect Time: 8000
Sched Delay: 1*TxInt: 3522100, 2*TxInt: 5, 3*TxInt: 0, GT 3*TxInt: 0
Registered protocols: isis bgp ospf
I want the values after Peer Addr, Intf and Registered protocols
Expected output
1.1.1.1
Port-Channel0.1
isis bgp ospf
This is what I have tried
grep -oP "Peer Addr\s+\K\w+"
I am unable to get the required output. I am new to shell scripting and would be great if someone can help me out on this one. I don't want all the output in a single command. I would like to store these as three different variables

With GNU awk for multi-char RS:
$ awk -v RS='[\n,]\\s*' 'sub(/^(Peer Addr|Intf|Registered protocols:) /,"")' file
1.1.1.1
Port-Channel0.1
isis bgp ospf

With GNU grep:
grep -Po '(Peer Addr|Intf|protocols:) \K.*?(?=(,|$))' file
Output:
1.1.1.1
Port-Channel0.1
isis bgp ospf
-P PATTERN: Interpret PATTERN as a Perl regular expression.
-o: Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.
\K: If \K appears in a Perl regex, it causes the regex matcher to drop everything before that point.
(?=...): The captured match must be followed by , or end of line ($) but that part is dropped, too.

With sed, you could do something like this:
sed -rn 's/Peer Addr *([^,]+), */\1/;
s/Intf *([^,]+),.*/\n\1/;
s/Registered protocols: *//;
ta;d;:a p'

awk -F"[, ]" '/^Peer Addr/{print $3"\n"$6} /^Registered protocols:/{sub(/Registered protocols: /,"");print}' File
Here IFS as , and .
/^Peer Addr/{print $3"\n"$6} : If a line starts with Peer Addr print $3( Here 1.1.1.1) and $6( Port-Channel0.1).
/^Registered protocols:/{sub(/Registered protocols: /,"");print}': If a line starts with Registered protocols: print entire line after removing the string Registered protocols: with sub awk function.

Related

How to filter the first Ip from a 5 tuple that is present in every line in Linux

I would like to filter the first IP from the given output below.
3676798 I/H 628 71.1.219.106 10198 71.1.208.58 20198 UDP A
3676798 R/U 628 71.1.208.58 20198 71.1.219.106 10198 UDP A
3676799 I/H 1066 71.1.57.57 10008 71.1.57.53 20008 UDP A
3676799 R/U 1066 71.1.57.53 20008 71.1.57.57 10008 UDP A
3676800 I/H 532 71.1.213.104 10142 71.1.203.52 20142 UDP A
3676800 R/U 532 71.1.203.52 20142 71.1.213.104 10142 UDP A
Using the below command, I'm able to get all the IP's but I would like to get only the first ip from each line which has 2 IP's. There may be line which might not have IP's at all. so I would like to ignore that line.
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' <filename>
I tried to use Awk to get it (as suggested in one of the comments) but this was unsuccessful since it prints the fourth field from any line which does not have IP's as well.
awk '{ print $4 }' file
Any pointers would be highly helpful.
To only print when the fourth field matches an IP address,
awk '$4 ~ /^[1-9][0-9]{1,2}(\.[0-9]{1,3}){3}$/{ print $4 }' file
To only print the first IP address on any line, regardless of where exactly it's found, try Perl:
perl -ne 's/.*?([0-9]{1,3}(\.[0-9]{1,3}){3}).*/$1/ and print' file
This looks quite similar to sed but the non-greedy .*? is not available in sed. If you wanted the last IP address on every line, it would be easy to do with sed.

Need to grep only IP Address

nslookup google.com
Server: xx.xx.xx.xx
Address: xx.xx.xxx.xx#53
Non-authoritative answer:
Name: google.com
Address: 172.217.164.110
I just need the last IP Address with grep/awk like below, please help.
172.217.164.110
Can be enhanced, but it'll do what you want:
nslookup google.com | sed -n '/Name:/{x;n;p;d;}; x' | awk '{print $2}'
Output (when only one Name+Address block is returned by nslookup):
172.217.164.110
I used sed's pattern space advanced options, printing the line following the "Name: google.com" line (x;n;p; sequence after "Name:" pattern match). I am not a sed guru, I used this Unix Stack Exchange answer, then awk to get only the IP following a whitespace.
In a IPv6 setting, you can get both the IPv4 and the IPv6 addresses, in two lines, so if this is not what you want, you will have to filter out the IPv6, using a pattern that only matches the format of an IPv4.

How do you extract IP addresses from a line matching a string from a file in bash?

Example:
I have a file name example.txt and inside it this text:
some text here INFO 200 cv 58687 http://saomesitehoere.com live connect ASDFG 61.215.80.6 07:16
some text here INFO 100 fv 582702687 http://saomesitehoere.org live connect 31.15.80.1 07:16:33
some text here INFO 00 ov 587 http://saomesitehoere.uk live connect ASGGGGFG 91.211.80.6 09:16
some text here INFO 800 kcv 277 http://saomesitehoere.za live connect AFG 71.215.81.5 09:14
I want to extract the IP-address from the line which contain the string name "ASDFG", meaning 61.215.80.6
Anyone can help ?
$ grep -oP 'ASDFG \K\S*' < file
61.215.80.6
you can try awk:
awk '/\<ASDFG\>/{print $(NF-1)}' file
you can use :
grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
An IP address is like three times one to three digits followed by a dot, followed by one to three digits. So this will give you the IP address in such a line:
$ grep ASDFG logfile | grep -o '\([[:digit:]]\{1,3\}\.\)\{3\}[[:digit:]]\+'
61.215.80.6
To extract all the "valid" IP addresses from the file:
gawk '{match($0,/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/,a);split(a[0],b,".")} b[1]<=255&& b[2]<=255 && b[3]<=255 && b[4]<=255 &&length(a[0]){print a[0]}' input_file
This will work in two steps:
First it will extract all the sub-strings having pattern line digits.digits.digits.digits
it will check if each set of digits is less then or equal to 255.
Note: needs gawk to use match() function.

Portable way to resolve host name to IP address

I need to resolve a host name to an IP address in a shell script. The code must work at least in Cygwin, Ubuntu and OpenWrt(busybox).
It can be assumed that each host will have only one IP address.
Example:
input
google.com
output
216.58.209.46
EDIT:
nslookup may seem like a good solution, but its output is quite unpredictable and difficult to filter. Here is the result command on my computer (Cygwin):
>nslookup google.com
Unauthorized answer:
Serwer: UnKnown
Address: fdc9:d7b9:6c62::1
Name: google.com
Addresses: 2a00:1450:401b:800::200e
216.58.209.78
I've no experience with OpenWRT or Busybox but the following one-liner will should work with a base installation of Cygwin or Ubuntu:
ipaddress=$(LC_ALL=C nslookup $host 2>/dev/null | sed -nr '/Name/,+1s|Address(es)?: *||p')
The above works with both the Ubuntu and Windows version of nslookup. However, it only works when the DNS server replies with one IP (v4 or v6) address; if more than one address is returned the first one will be used.
Explanation
LC_ALL=C nslookup sets the LC_ALL environment variable when running the nslookup command so that the command ignores the current system locale and print its output in the command’s default language (English).
The 2>/dev/null avoids having warnings from the Windows version of nslookup about non-authoritative servers being printed.
The sed command looks for the line containing Name and then prints the following line after stripping the phrase Addresses: when there's more than one IP (v4 or 6) address -- or Address: when only one address is returned by the name server.
The -n option means lines aren't printed unless there's a p commandwhile the-r` option means extended regular expressions are used (GNU sed is the default for Cygwin and Ubuntu).
If you want something available out-of-the-box on almost any modern UNIX, use Python:
pylookup() {
python -c 'import socket, sys; print socket.gethostbyname(sys.argv[1])' "$#" 2>/dev/null
}
address=$(pylookup google.com)
With respect to special-purpose tools, dig is far easier to work with than nslookup, and its short mode emits only literal answers -- in this case, IP addresses. To take only the first address, if more than one is found:
# this is a bash-specific idiom
read -r address < <(dig +short google.com | grep -E '^[0-9.]+$')
If you need to work with POSIX sh, or broken versions of bash (such as Git Bash, built with mingw, where process substitution doesn't work), then you might instead use:
address=$(dig +short google.com | grep -E '^[0-9.]+$' | head -n 1)
dig is available for cygwin in the bind-utils package; as bind is most widely used DNS server on UNIX, bind-utils (built from the same codebase) is available for almost all Unix-family operating systems as well.
Here's my variation that steals from earlier answers:
nslookup blueboard 2> /dev/null | awk '/Address/{a=$3}END{print a}'
This depends on nslookup returning matching lines that look like:
Address 1: 192.168.1.100 blueboard
...and only returns the last address.
Caveats: this doesn't handle non-matching hostnames at all.
TL;DR; Option 2 is my preferred choice for IPv4 address. Adjust the regex to get IPv6 and/or awk to get both. There is a slight edit to option 2 suggested use given in EDIT
Well a terribly late answer here, but I think I'll share my solution here, esp. because the accepted answer didn't work for me on openWRT(no python with minimal setup) and the other answer errors out "no address found after comma".
Option 1 (gives the last address from last entry sent by nameserver):
nslookup example.com 2>/dev/null | tail -2 | tail -1 | awk '{print $3}'
Pretty simple and straight forward and doesn't really need an explanation of piped commands.
Although, in my tests this always gave IPv4 address (because IPv4 was always last line, at least in my tests.) However, I read about the unexpected behavior of nslookup. So, I had to find a way to make sure I get IPv4 even if the order was reversed - thanks regex
Option 2 (makes sure you get IPv4):
nslookup example.com 2>/dev/null | sed 's/[^0-9. ]//g' | tail -n 1 | awk -F " " '{print $2}'
Explanation:
nslookup example.com 2>/dev/null - look up given host and ignore STDERR (2>/dev/null)
sed 's/[^0-9. ]//g' - regex to get IPv4 (numbers and dots, read about 's' command here)
tail -n 1 - get last 1 line (alt, tail -1)
awk -F " " '{print $2} - Captures and prints the second part of line using " " as a field separator
EDIT: A slight modification based on a comment to make it actually more generalized:
nslookup example.com 2>/dev/null | printf "%s" "$(sed 's/[^0-9. ]//g')" | tail -n 1 | printf "%s" "$(awk -F " " '{print $1}')"
In the above edit, I'm using printf command substitution to take care of any unwanted trailing newlines.

Best way to handle objects property:value in the stdin in bash

Bonjour,
I launch nslookup someServer. I consider I get a serie of object (as in powers hell) separated by empty lines and not simply a stdout.
$ nslookup someServer
Server: 10.0.0.1
Address: 10.0.0.1#53
Name: someServer
Address: 10.0.0.5
$
How to get the object who have both properties Name and Address?
nslookup someServer | haveboth Name Address | wc -l
Does it exists in GNU utilities?
are you simply looking for a way to check that you have both of these values in your output?
Then you could use perl in oneline mode (probably not the most pretty solution imaginable, but does what you want and could be expanded to check more things easily)
nslookup someServer | perl -ne '$v{"NAME"}++ if /Name/; $v{"ADDRESS"}++ if /Address/; END{ print "Has both values\n" if $v{"NAME"} && $v{"ADDRESS"} }'
this goes through your output and counts the occurrences of Name and Address and then prints a message if it has more than zero of both.
If using perl in this way is a viable option, then I can recommend this page for further reading on perl oneliners
EDIT:
in case you want to have access to the values that are stored in your property: you can use
nslookup someServer | perl -F: -lane '$v{"NAME"} = $F[1] if /Name/; $v{"ADDRESS"} = $F[1] if /Address/; END{ print $v{"ADDRESS"}." ".$v{"NAME"} if exists $v{"ADDRESS"} && $v{"NAME"} }'
This will split a given line on : as field separator and store the value in the variable instead of simple counting occurrences. Note that done in this way it would only store the last occurrence.

Resources