This post was originally published on this site

Tired of formatting the tnsnames.ora to make it more readable, I have taken the nice awk examples from Jeremy Schneider: https://ardentperf.com/2008/11/28/parsing-listenerora-with-awk-and-sed/ and created a function to format all files .ora (lisp-like config files).

Example, before:

$ cat tnsnames.ora
LUDOCDB1_SITE2 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP )(HOST = newbox02 )(PORT = 1522 ))
    (CONNECT_DATA =
      (SERVICE_NAME = LUDOCDB1_SITE2_DGMGRL )
    )
  )
LUDOCDB1_SITE1 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP )(HOST = newbox01 )(PORT = 1522 )) (CONNECT_DATA = (SERVICE_NAME = LUDOCDB1_SITE1_DGMGRL )
    )
  )
LUDOCDB1 = (DESCRIPTION = (CONNECT_TIMEOUT = 5 )(TRANSPORT_CONNECT_TIMEOUT = 3 ) (ADDRESS_LIST = (LOAD_BALANCE = OFF ) (ADDRESS = (PROTOCOL = TCP )(HOST = newbox01 )(PORT = 1522 )) (ADDRESS = (PROTOCOL = TCP )(HOST = newbox02 )(PORT = 1522 ))) (CONNECT_DATA = (SERVICE_NAME = LUDOCDB1_RW )))

# read only:
LUDOCDB1_RO =
  (DESCRIPTION =
    (CONNECT_TIMEOUT = 5 )(TRANSPORT_CONNECT_TIMEOUT = 3 )
    (ADDRESS_LIST = (LOAD_BALANCE = OFF )
      (ADDRESS = (PROTOCOL = TCP )(HOST = newbox02 )(PORT = 1522 ))
      (ADDRESS = (PROTOCOL = TCP )(HOST = newbox01 )(PORT = 1522 ))
    ) (CONNECT_DATA =
      (SERVICE_NAME = LUDOCDB1_RO )
    )
  )

and after:

# cat tnsnames.ora | tidy_dotora
LUDOCDB1_SITE2=
   (DESCRIPTION=
     (ADDRESS=
       (PROTOCOL=TCP)
       (HOST=newbox02)
       (PORT=1522)
     )
     (CONNECT_DATA=
       (SERVICE_NAME=LUDOCDB1_SITE2_DGMGRL)
     )
   )

LUDOCDB1_SITE1=
   (DESCRIPTION=
     (ADDRESS=
       (PROTOCOL=TCP)
       (HOST=newbox01)
       (PORT=1522)
     )
     (CONNECT_DATA=
       (SERVICE_NAME=LUDOCDB1_SITE1_DGMGRL)
     )
   )

LUDOCDB1=
   (DESCRIPTION=
     (CONNECT_TIMEOUT=5)
     (TRANSPORT_CONNECT_TIMEOUT=3)
     (ADDRESS_LIST=
       (LOAD_BALANCE=OFF)
       (ADDRESS=
         (PROTOCOL=TCP)
         (HOST=newbox01)
         (PORT=1522)
       )
       (ADDRESS=
         (PROTOCOL=TCP)
         (HOST=newbox02)
         (PORT=1522)
       )
     )
     (CONNECT_DATA=
       (SERVICE_NAME=LUDOCDB1_RW)
     )
   )
# read only:

LUDOCDB1_RO=
   (DESCRIPTION=
     (CONNECT_TIMEOUT=5)
     (TRANSPORT_CONNECT_TIMEOUT=3)
     (ADDRESS_LIST=
       (LOAD_BALANCE=OFF)
       (ADDRESS=
         (PROTOCOL=TCP)
         (HOST=newbox02)
         (PORT=1522)
       )
       (ADDRESS=
         (PROTOCOL=TCP)
         (HOST=newbox01)
         (PORT=1522)
       )
     )
     (CONNECT_DATA=
       (SERVICE_NAME=LUDOCDB1_RO)
     )
   )

The AWK script:

tidy_dotora () {
 
# Heavily based on Jeremy Scheider's work:
# https://ardentperf.com/2008/11/28/parsing-listenerora-with-awk-and-sed/
#
# you can source this function in bash and use it like:
# cat $TNS_ADMIN/listener.ora | tidy_dotora 
 
awk '
 
# function for padding
function pad (string, len, char) {
	ret = string;
	for ( padi = length(string); padi<len ; padi++) {
			ret = sprintf("%s%s",ret,char);
	}
	return ret;
}
	
BEGIN {
	level=1;
	first=1;
	lastcomment=0;
}
	
{
#MAIN
 
	# just skip any comments and print as is
	if ($0 ~ "^[[:space:]]*#") {
		if (lastcomment==0) {
			printf("n");
		}
		print;
		lastcomment=1;
		next;
	}
	lastcomment=0;
	
	# this puts every occurrence of =, ( and ) in different tokens
	gsub(/=/,"`=");
	gsub(/(/,"`(");
	gsub(/)/,"`)");
	split($0,tokens,"`");
 
	i=1; while(i in tokens) {
 
		# trim token and continue if empty
		gsub(/ /, "",tokens[i]);
		if(!tokens[i]) {i++; continue;}
 
		# got ( "open bracket": new level begins
		# increase the level, newline and pad
		if(tokens[i]~"^[(]") {
			level++;
			printf ("n");
			printf (pad("", 2*level-1, " "));
		}
	
		# got ) "close bracket" : level ends
		# decrease the level but newline only if another one was closed immediately before
		if(tokens[i]~"^[)]") {
			level--;
			if (wentdown==1) {
				printf("n");
				printf (pad("", 2*level+1, " "));
			}
			wentdown=1;
		} else {
			wentdown=0;
		}
	
		# if level==1 and is alphanumeric, it is a "TOP" entry (LISTENER, SID_LIST_LISTENER or property)
		# add extra line (and eventually track it for other usage)
		if(level==1 && i==1 && tokens[i]~"[A-Za-z]") {
			TOP=tokens[i];
			if (first==1) {
				first=0;
			} else {
				printf "nn";
			}
		}
 
		printf (tokens[i]);
		i++;
	}
}
END {
	# new line at the end of file
	printf("n");
}' 
}

I have included the function in the COE github repo. More functions to come (hopefully).

Ludo