Merge multiple lines to 1 row with awk(or familiar)

Viewed 128

i need to merge multiple lines from the output of nmap into a single row

FROM:

Nmap scan report for example.com
22/tcp   open  ssh
80/tcp   open  http
111/tcp  open  rpcbind
1720/tcp open  h323q931
5432/tcp open  postgresql
Nmap scan report for example.com 
22/tcp   open  ssh
80/tcp   open  http
81/tcp   open  hosts2-ns
111/tcp  open  rpcbind
1720/tcp open  h323q931
Nmap scan report for example.com
22/tcp   open  ssh
111/tcp  open  rpcbind
1720/tcp open  h323q931
Nmap scan report for example.com
22/tcp   open     ssh

TO:

Nmap scan report for example.com
22/tcp   open  ssh 80/tcp   open  http 111/tcp  open  rpcbind 1720/tcp open  h323q931 5432/tcp open  postgresql
Nmap scan report for example.com 
22/tcp   open  ssh 80/tcp   open  http 81/tcp   open  hosts2-ns 111/tcp  open  rpcbind 1720/tcp open  h323q931
Nmap scan report for example.com 
22/tcp   open  ssh 111/tcp  open  rpcbind 1720/tcp open  h323q931
Nmap scan report for example.com 
22/tcp   open     ssh

For 1 host there should be 2 lines. 1st one is starting with Nmap scan ... and the other row is open port info. I saw many questions about multiple lines into 1 line with awk but could'not come up with something.

3 Answers

Why not this:

awk '$0~"Nmap"{if(output!="")print output;print;output=""}$0!~"Nmap"{output=output""$0" "}END{if(output!="")print output}'

If $0 has "Nmap", print that line. If not, keep track of what is happening, then print it when a new Nmap.

The END{} block takes care of firing what was not printed. Note that you are looking for Nmap to print something out. Also note the condition $0!~"Nmap" to avoid buffering that first line as well.

Could you please try following, written and tested with shown samples in GNU awk.

awk '{printf("%s%s%s",$0~/^[0-9]/?"":(FNR!=1?ORS:""),$0,$0~/^Nmap/?ORS:"")} END{print ""}' Input_file

OR a non-one liner form for better understanding.

awk '
{
  printf("%s%s%s",$0~/^[0-9]/?"":\
        (FNR!=1?ORS:""),$0,\
        $0~/^Nmap/?ORS:"")
}
END{ print "" }
' Input_file

Explanation: Adding detailed explanation for above solution its only for understanding purposes not for running.

awk '                                    ##Starting awk program from here.
{                                        ##Using printf for checking 3 conditions and as per that print things.
  printf("%s%s%s",$0~/^[0-9]/?"":\       ##First checking if a line starts with digit then print NULL OR if its first line print NULL else print new line.
        (FNR!=1?ORS:""),$0,\             ##Checking condition if line is NOT first line then print new line else print NULL along with current line.
        $0~/^Nmap/?ORS:"")               ##Checking condition if line starts from Nmap then print new line or print NULL here.
}
END{ print "" }                          ##Staring END block of this code here and printing null line here.
' Input-file                             ##Mentioning Input_file name here.

Alternative using GNU sed:

$ sed -n '1{h;d}; /^Nmap/{x;s/\n/ /2g;p;d}; H; ${x;s/\n/ /2g;p}' input.txt
Nmap scan report for example.com
22/tcp   open  ssh 80/tcp   open  http 111/tcp  open  rpcbind 1720/tcp open  h323q931 5432/tcp open  postgresql
Nmap scan report for example.com
22/tcp   open  ssh 80/tcp   open  http 81/tcp   open  hosts2-ns 111/tcp  open  rpcbind 1720/tcp open  h323q931
Nmap scan report for example.com
22/tcp   open  ssh 111/tcp  open  rpcbind 1720/tcp open  h323q931
Nmap scan report for example.com
22/tcp   open     ssh
Related