Home > other >  sed append comma to each line, except the last line in the block
sed append comma to each line, except the last line in the block

Time:11-01

I am writing a utility script to fix a Verilog code using sed. What I'm trying to do is converting code as follows

input file

my_instance_name
(
   .port1     (port1    )
   .port2     (port2    ) // comment for port line 2
   .port3     (port3    )
   .port4     (port4    ) // comment for port line 4
);

What I want

my_instance_name 
(
   .port1     (port1    ),
   .port2     (port2    ), // comment for port line 2
   .port3     (port3    ), 
   .port4     (port4    ) // comment for port line 4
);

The points are

  1. Append comma just after the closing parenthesis of each 'port' lines
  2. Do not append comma for the last 'port'
  3. Preserve the comments if exist (comment starts with double-slash like C)
  4. Whitespaces (except the line breaks) are not important. it is only for human readability.
  5. There can be multiple linebreaks after the last port line

If the sed script becomes too complicated, it is also OK to just prepend the comma just before the dot, except the first port line. i.e, the following code is OK although it is not pretty.

my_instance_name 
(
   .port1     (port1    )
   ,.port2     (port2    ) // comment for port line 2
   ,.port3     (port3    )
   ,.port4     (port4    ) // comment for port line 4
);

I could only manage appending commas to all 'port' lines via the following two line

sed -i /^\s*my_instance_name.*/,/);/{s/^\(\s*\..*)\)\(\s*\/\/.*\)/\1,\2/} my_verilog.txt
sed -i /^\s*my_instance_name.*/,/);/{s/^\(\s*\..*)\)\s*\$/\1,/} my_verilog.txt

It detects the code scope from "my_instance_name" to ");". The first line insert comma between the closing parenthesis and comment. The second line is for port lines without comments.

But I have no idea how to exclude the last line.

CodePudding user response:

This would probably be significantly easier in Awk.

awk '/my_instance_name/ { p=1 }
  p && /\);/ { if (prev) print prev; print; p=0; next }
  p && /port/ { if (prev) { sub(/\)/, "),", prev); print prev; }
    prev = $0; next } 1' my_verilog.txt

Standard Awk does not have an option like the -i option of sed; this will simply print the output to standard output, which you can redirect to a temporary file and then move back on top of the original. Or if you have GNU Awk, it has -i inplace which works like sed's -i.

CodePudding user response:

Using GNU sed

$ sed -Ezi.bak 's/\([^)]*\)/&,/g;s/(\([^)]*\)),([^\n]*\n\);)/\1\2/g' input_file
my_instance_name
(
   .port1     (port1    ),
   .port2     (port2    ), // comment for port line 2
   .port3     (port3    ),
   .port4     (port4    ) // comment for port line 4
);

CodePudding user response:

You may use this gnu-awk solution using custom RS to split shown block on header and footer:

awk -i inplace -v RS='my_instance_name\\s*\\(\\s*' '
NF {
   $0 = gensub(/(\n\s*)([^)])/, "\\1,\\2", "g")
}
{ORS=RT} 1' file

cat file

my_instance_name
(
   .port1     (port1    )
   ,.port2     (port2    ) // comment for port line 2
   ,.port3     (port3    )
   ,.port4     (port4    ) // comment for port line 4
);

Note that due to use of -i inplace changes will be saved inline in same input file.

Breakdown:

  • -v RS='my_instance_name\\s*\\(\\s*' splits input data by my_instance_name followed by 0 or more whitespaces then ( and again followed by 0 or more whitespaces.
  • gensub function matches newline followed by 0 or more whitespaces and captures in a group #1. It matches any character that's not ) (to avoid matching ); line) and captures in a group #2. In replacement we insert a comma between these 2 values.
  • {ORS=RT} populates ORS same as the text matched by RS pattern i.e. RT
  • 1 prints all records

For appending commas in the end use similar awk:

awk -v RS='my_instance_name\\s*\\(\\s*' '
NF {
   $0 = gensub(/\)([^\n]*\n[^)])/, "),\\1", "g")
}
{ORS=RT} 1' file

my_instance_name
(
   .port1     (port1    ),
   .port2     (port2    ), // comment for port line 2
   .port3     (port3    ),
   .port4     (port4    ) // comment for port line 4
);
  • Related