How to parse..

Help, I need to get the port number of a Oracle database using the tnsping command. I need to parse it's output.

=====================
Attempting to contact (ADDRESS=(PROTOCOL=TCP)(Host=chamar)(Port=1541))

Sometimes may be like this:

Attempting to contact (ADDRESS=(COMMUNITY=TCP.WORLD)(PROTOCOL=TCP)(Host=chamar)(Port=1541))

Also, sometimes Port will be PORT or port.

I tried using awk and delimiting by = but sometimes there are more = than I expect..

Thanks for your help.

You could use the substr function of awk:

awk '{ print substr ($0, length ($0) - 5, 4)}' someFile

or:

port="Attempting to contact (ADDRESS=(PROTOCOL=TCP)(Host=chamar)(Port=1541))"
echo $port | awk '{ print substr ($0, length ($0) - 5, 4)}'

tnsping PROD | awk '/Attempting/ {
gsub("[()=]"," ")
$0=toupper($0)
for (i=1;i<=NF;i++)
   if ($i=="PORT")
       {print $(i+1)
        exit}
}' | read port

echo "port=$port"

Above script simplifies the parsing task by first cleaning up the line, then scans the line for PORT and prints the word after. Port number can be more than 4 digits.

I think the line of interest is always just one line, correct? You show one sample as being on two lines. If so, just change the first line to:

tnsping PROD | awk '{

and it will parse each line until PORT is found.

Great, thanks very much for the replies.

I'll try both ways. I was stuck.

Assuming your awk is nawk, you can (probably) do this:

tnsping PROD | awk '/Attempting/ {
        s = toupper($0)
        if (!match(s, /PORT=[0-9]+/)
            print "Where's the port?" > "/dev/tty"
            exit(1)
        else
            port = substr(s, RSTART+5, RLENGTH-5)
    }' | read port
echo "port = " $port

The regexp may need to be adjusted to /PORT=[0-9][0-9]*/ if your awk doesn't like the +.

If your output may actually come out on multiple lines per the formatting of your original post, but the "port=" and its port number will always be on the same line, you can shortcut the above this way:

tnsping PROD | awk 'match(toupper($0), /PORT=[0-9]+/) {
        print substr($0, RSTART+5, RLENGTH-5)
}' | read port
echo "port = " $port

The secret here is to realize that the pattern does not have to be a regular expression. Be a little careful: There's only one RSTART/RLENGTH pair, so more than one "match" in an expression (in the pattern or the action) will only get set RSTART & RLENGTH for the last "match" evaluated.

I like to use awk because its pretty powerful, but I also like to try to achieve the same results with other tools. Here is my bid which works every time:

$ echo "Attempting to contact (ADDRESS=(PROTOCOL=TCP)(Host=chamar)(Port=1541))"
|tr -d ")" | cut -d"(" -f5 | cut -d"=" -f2

Good luck!

This is even better:

$ echo "Attempting to contact (ADDRESS=(PROTOCOL=TCP)(Host=chamar)(Port=1541))"
|tr -d ")" | cut -d"=" -f5

Should've thought of this the first time.

Oops you're right about the case where COMMUNITY= is in the stream. Try this, it works:

$ echo "Attempting to contact (ADDRESS=(COMMUNITY=PROTOCOL=TCP)(Host=chamar)(Po
rt=1541))"|tr -d ")" | sed -e "s/COMMUNITY=//g" | cut -d"=" -f5

Sorry for the confusion

This does it all in a single sed command.

echo "Attempting to contact (ADDRESS=(COMMUNITY=PROTOCOL=TCP)(Host=chamar)(Port=1541))" |
sed 'y/port/PORT/
s/.*PORT=\([1-9][0-9]*\).*/\1/
t
d'