sed command to print a string after certain pattern

Viewed 296

Trying to print a string using sed after a line/statement as below,

class name extends #(p val1 = 10,
                     p val2 = 20,
                     p val3 = 30) f_name;


"class" and "extends" and ";" at the end of that line/statement (ie, consuming multiple lines) will remain constant, rest of the other terms are random.

Expected Result:

class name extends #(p val1 = 10,
                     p val2 = 20,
                     p val3 = 30) f_name;

string t;

Trial done as below,

Trial_command:

sed -i '/^class/a\\string t;'

Trial_Result:

class name extends #(p val1 = 10,
string t;
                     p val2 = 20,
                     p val3 = 30) f_name;


The above command works proper if the class statement ends in the same line as below

class name extends f_name;

But not working when its consumes multiple lines.

Please suggest on this.

6 Answers

Multiline techniques must be used. With GNU sed:

sed -E '/^\s*class\s+\w+\s+extends\b/{ :a; /;\s*$/!{N; ba}; s/$/\nstring t;/ }' file
perl -lpe '$class=1 if /^class/; if ($class and /;$/) { $_ .= "\nstring t;"; $class = 0 }` file

The above command remembers when a line starting with class is seen. Then adds a line string t; and forgets that class is seen, when it sees a line ending with ;.

See https://perldoc.perl.org/perlrun.html for help on various options used.

Ed can do this.

#!/bin/sh

cat >> append.ed << EOF
\$
a

string t;
.
wq
EOF

ed -s filename < append.ed
rm -v ./append.ed

With GNU sed for -z (to read the whole input as a single string) and for \n to mean newline in the replacement text:

$ sed -z 's/class name extends[^;]*;/&\n\nstring t;/' file
class name extends #(p val1 = 10,
                     p val2 = 20,
                     p val3 = 30) f_name;

string t;

otherwise using any awk in any shell on every UNIX box:

$ awk '
    { r = (NR>1 ? r ORS : "") $0 }
    END { sub(/class name extends[^;]*;/,"&\n\nstring t;",r); print r }
' file
class name extends #(p val1 = 10,
                     p val2 = 20,
                     p val3 = 30) f_name;

string t;

This perl one-liner does the job:

perl -0777 -ape 's/\bclass \w+ extends[\s\S]+?;\K/\nstring t;/' file
class name extends #(p val1 = 10,
                     p val2 = 20,
                     p val3 = 30) f_name;
string t;

Explanation:

s/                # substitute
    \b              # word boundary
    class           # literally
    \w+             # 1 or more word characters (class name)
    extends         # literally
    [\s\S]+?        # 1 or more any character including newline
    ;               # semi colon
    \K              # forget all we have seen until this position
/                 # with
    \n              # linefeed
    string t;       # literally
/                 # end subs

This might work for you (GNU sed):

sed '/^class/!b;:a;/;$/!{N;ba};/extends/astring t;' file

If the current line does not begin class, not interested.

If the current line does not end ;, append the next line and repeat.

If the current line(s) contains extended, append another line string t;.


If a blank line then a line containing string t; is required, either:

sed '/^class/!b;:a;/;$/!{N;ba};/extends/G;//astring t;' file

or:

sed '/^class/!b;:a;/;$/!{N;ba};/extends/a\\nstring t;' file
Related