I have a source file where I use a macro to do logging. Typical logging lines look like:
STDOUT_LOG(logDEBUG4) << \"a single line log text\" << aVari
In the end the command that successfully did what I wanted was the following:
perl -0777pne 's/STDOUT_LOG\((.*?)\)(.*?)<<(.*?);/STDOUT_LOG\(\1\,\2\3\);/gs' sensor.cpp
This is alot a superposition for the answers of "Federico Piazza" and "zdim". You'll notice I prepended the "STDOUT_LOG" bit in the front to really much exactly what I wanted in a large number of source files. I noticed that for correct matching one MUST include the /s
operator at the end of the regex , but also add the -0777
flag in the perl executable.
see perlrun for an explanation of the runtime perl executable flags
You can use a regex like this with s
(single line flag):
\).*?<<(.*?);
And the replacement string:
, \1);
Working demo
With $log
string containing the whole log, with newlines and all,
$log =~ s/STDOUT_LOG\K \( ( [^)]+ ) \) ( [^;]+ );/($1,$2);/xs;
The [^)]+
matches up to the first closing parentheses, and likewise [^;]+
goes to the first ;
. You do not need nor want the /g
modifier. The /x
allows spaces for readability.
The \K
is a particular form of the positive lookbehind, which also discards all previous matches. So the pattern before it, used as an assertion, is not affected by the substitution. See it in perlretut. It is not essential here, but if you leave it out then STDOUT_LOG
is consumed by the match and so it need be typed in as well in the replacement part. It's just cleaner this way.
Since there could be a problem with how the log is input here is a complete program
use warnings 'all';
use strict;
my $logfile = 'log.txt';
my $log = do {
local $/;
open my $fh, '<', $logfile or die "Can't open $logfile: $!";
<$fh>;
};
# print $log;
$log =~ s/STDOUT_LOG\K \( ([^)]+) \) ([^;]+);/($1,$2);/xs;
print $log;
The do
block allows us to slurp the whole file $logfile
into the $log
variable, by unsetting $INPUT_RECORD_SEPARATOR
(or $/
), within the block by local. See discussions in perlvar.
If this is meant to be done with a one-liner
perl -0777 -pe 's/STDOUT_LOG\K \( ([^)]+) \) ([^;]+);/($1,$2);/xs;' log.txt
The -0777
makes it slurp the whole file into $_
, and the substitution is by default done on it. The -p
also prints $_
after each line is processed (and here we only have one "line" -- the whole file). See perlrun for command line switches and General Variables in perlvar for $_
.
Both these assume that the shown example of a log (one-liner or the multi-line one) stands on its own, that there is a single log-string to process. For simplicity I took that to be in a file log.txt
and read it from that file, into the $log
variable in the script and into $_
in the one-liner.
Here is a way to do it:
use Modern::Perl;
my @lines = (
'STDOUT_LOG(logDEBUG4) << "a single line log text" << aVariable;',
' STDOUT_LOG(logDEBUG4)
<< bsd->sensorName
<< " trainMinValueInWin = " << value.trough
<< " trainMaxValueInWin = " << value.peak
<< " |";'
);
for (@lines) {
s/(STDOUT_LOG\(.*?)\)(\s*)<<(.*?);/$1, $2$3);/gs;
say;
}
Output:
STDOUT_LOG(logDEBUG4, "a single line log text" << aVariable);
STDOUT_LOG(logDEBUG4,
bsd->sensorName
<< " trainMinValueInWin = " << value.trough
<< " trainMaxValueInWin = " << value.peak
<< " |");