-1

I have a file that is full of lines like:

snprintf(log_buffer,...
             ...
             ...
             ...
             ); 

If I use sed, I can find these lines with "snprint.*log_buffer".

My issue is, I would like to add a line after the ; of the statement:

send_to_logger_process(log_buffer); 

However I cannot blindly add a line to the line after the match string, as the snprintf line can be multiple lines.

Any idea of how to make this work for sed or awk, or some other command line tool so I can do it to all the files in a directory? You can assume that there are no semicolons (;) within the body of the snprintf: the only ; is at the end of the statement.

edit 01: the solution:

sed '/snprint.*log_buffer/,/;/ s/;/;\nsend_to_logger_process(log_buffer);/'

has some false positives:

      static struct option long_options[] =
        {
          /* These options set a flag. */
          {"verbose", no_argument,       &verbose_flag, 1},
          {"brief",   no_argument,       &verbose_flag, 0},
          /* These options dont set a flag.
             We distinguish them by their indices. */
          {"chipaddr_led",    required_argument, 0, 'c'},
          {"togglepaddr", required_argument, 0, 't'},
          {"regaddr",     required_argument, 0, 'r'},
          {"value",       required_argument, 0, 'v'},
          {"i2cport",     required_argument, 0, 'i'},
          {"led",         required_argument, 0, 'l'},
          {"mstime",      required_argument, 0, 'm'},
          {"dutycyle",    required_argument, 0, 'd'},
          {"status",      required_argument, 0, 's'},
          {0, 0, 0, 0}
        };
send_to_logger_process(log_buffer);

5 Answers 5

2

Perhaps:

awk '
    /snprintf\(log_buffer/ { m=1 }
    m && sub(/\);/, "&\nsend_to_logger_process(log_buffer);\n") { m=0 }
    1;
' file

Or:

awk '
    /snprintf\(log_buffer/, sub(/\);/, "&\nsend_to_logger_process(log_buffer);\n") {}
    1;
' file

Assumes there are not two snprintf on the same line.

A Perl approach that does handle multiple occurences on a single line:

perl -gpe 's/snprintf\(log_buffer[^;]+;/$&\nsend_to_logger_process(log_buffer);\n/g' file

A similar approach can be used with GNU sed with -z option as long as file is not too large.

Sign up to request clarification or add additional context in comments.

Comments

2

You can use an address range to only apply the substitution to the snprintf "block", and use the ; as the place where the new line should go, as it can't occur anywhere else.

sed '/snprint.*log_buffer/,/;/ s/;/;\nsend_to_logger_process(log_buffer);/' 

You can exclude single-line snprintf commands by specifying you're only interested in lines not containing a semicolon:

sed '/snprint.*log_buffer[^;]*$/,/;/ s/;/;\nsend_to_logger_process(log_buffer);/'
#                        ~~~~~~

11 Comments

I got some false positives: ``` if ( ten_hz_clock%100==0 ) { snprintf(log_buffer,255,"forever_ui: ten_hz_clock=%ldms,\n %ds\n",ten_hz_clock*100,ten_hz_clock/10);\n send_to_logger_process(log_buffer);\n fflush(stdout);\n send_to_logger_process(log_buffer);\n }; //then\n ```
I can't post with <cr><lf> the "fflush(stdout); line seems to be a match.
It's an interesting difference between sed and awk range expressions. Given input from printf 'snprintf(log_buffer,...);\nfoo;\n' if we use sed '/snprint.*log_buffer/,/;/ s/;/;\nsend_to_logger_process(log_buffer);/' the range expression wont find the ; at the end of the snprintf line itll find the one at the end of the foo line instead, whereas if we use awk '/snprint.*log_buffer/,/;/{sub(/;/,";\nsend_to_logger_process(log_buffer);")}1' it WILL find the ; at the end of the snprintf line as it starts matching the end regexp from the current line while sed starts on the next line
@EdMorton: And in Perl, there are two types of ranges: /r1/ .. /r2/ and /r1/ ... /r2/.
@choroba I'm surprised it's only 2 :-).
|
1

Using any awk:

$ awk '
    { print }
    /snprint.*log_buffer/ { f=1 }
    f && /;/ { print "send_to_logger_process(log_buffer);"; f=0 }
' file
snprintf(log_buffer,...
             ...
             ...
             ...
             );
send_to_logger_process(log_buffer);

Comments

0

This might work for you (GNU sed):

sed '/snprintf.*log_buffer/!b;/;/bb;:a;n;/;/!ba;:b;a add a line' file

If a line does not contains snprintf.*log dismiss it.

Otherwise if that same line contains a ; then add a line of your choice.

Otherwise, keep reading until a line containing ; and then add a line of your choice.

Comments

0

It’s quite easy with Raku/Sparrow, using range construction:

# find all blocks delimited by start / stop
# expressions 
between: { "sptintf(log_buffer" } { ";" }
:any:
end:

code: <<RAKU
!raku
# update all matched blocks
for streams().values -> $block {
  # line number of the bottom 
  # of a block 
  my $line-num = $block<>.tail()<index>;
  # update file in place appending required 
  # chunk
  replace(
    "/path/to/file.txt",
    $line-num,
    $block<>.tail()<data> ~ 
    "send_to_logger_process(log_buffer);"
  );
}
RAKU

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.