То, что вы хотите сделать, не тривиально. Требуется некоторый анализ, чтобы позаботиться о сбалансированных разделителях, кавычках и правиле C, чтобы смежные строковые литералы были объединены в один. К счастью, модуль Perl Text::Balanced обрабатывает многие из них (Text::Balanced доступен в стандартной библиотеке Perl). Следующий скрипт должен делать более или менее то, что вы хотите. Он принимает один аргумент командной строки и выводит на стандартный вывод. Вы должны будете обернуть его внутри сценария оболочки. Я использовал следующую обертку, чтобы проверить это:
#/bin/bash
find in/ -name '*.c' -exec sh -c 'in="$1"; out="out/${1#in/}"; perl script.pl "$in" > "$out"' _ {} \;
colordiff -ru expected/ out/
А вот и скрипт Perl. Я написал несколько комментариев, но не стесняйтесь спрашивать, если вам нужно больше объяснений.
use strict;
use warnings;
use File::Slurp 'read_file';
use Text::Balanced 'extract_bracketed', 'extract_delimited';
my $text = read_file(shift);
my $last = 0;
while ($text =~ /( # store all matched text in $1
\bprintf # start of literal word 'printf'
(\s*) # optional whitespace, stored in $2
(?=\() # lookahead for literal opening parenthesis
)/gx) {
# after a successful match,
# 1. pos($text) is on the character right behind the match (opening parenthesis)
# 2. $1 contains the matched text (whole word 'printf' followed by optional
# whitespace, but not the opening parenthesis)
# 3. $2 contains the (optional) whitespace
# output up to, but not including, 'printf'
print substr($text, $last, pos($text) - $last - length($1));
print "print_record$2(";
# extract and process argument
my ($argument) = extract_bracketed($text, '()');
process_argument($argument);
# save current position
$last = pos($text);
}
# output remainder of text
print substr($text, $last);
# process_argument() properly handles the situation of a format string
# consisting of adjacent string literals
sub process_argument {
my $argument = shift;
# skip opening parenthesis retained by extract_bracketed()
$argument =~ /^\(/g;
# scan for quoted strings
my $saved;
my $last = 0;
while (1) {
# extract quoted string
my ($string, undef, $whitespace) = extract_delimited($argument, '"');
last if !$string; # quit if not found
# as we still have strings remaining, the saved one wasn't the last and should
# be output verbatim
print $saved if $saved;
$saved = $whitespace . $string;
$last = pos($argument);
}
if ($saved) {
$saved =~ s/\\n"$/"/; # chop newline character sequence off last string
print $saved;
}
# output remainder of argument
print substr($argument, $last);
}