admin管理员组

文章数量:1355112

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);

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);
Share Improve this question edited Mar 31 at 18:26 Jonathan Leslie asked Mar 31 at 16:11 Jonathan LeslieJonathan Leslie 356 bronze badges
Add a comment  | 

5 Answers 5

Reset to default 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.

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);/'
#                        ~~~~~~

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);

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.

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

本文标签: