Home > Mobile >  Replace pattern within quotes which appear more than once
Replace pattern within quotes which appear more than once

Time:09-04

I have a pattern which is -CFLAGS "some text". I want to extract this to another file and also replace it with nothing.

(1) replace: I tried out the command shown below to replace the pattern but the output is not as expected.

echo 'version: -CFLAGS "8.x-1.0-alpha1 beta1 gama1" versions -CFLAGS "sudy monday" sudy1 -LDFLAGS "sim" sunday' 
| perl -pi -e 's/(.*)(\-CFLAGS \".*\")(.*)/$1$3/g'

output: version: -CFLAGS "8.x-1.0-alpha1 beta1 gama1" versions sunday

$2 is -CFLAGS "sudy monday" sudy1 -LDFLAGS "sim". I want all occurrences of -CFLAGS "*" only to be removed.

(2) extract: I tried the below command to extract -CFLAGS "*". I think this is not efficient.

echo 'version: -CFLAGS "8.x-1.0-alpha1 beta1 gama1" versions -CFLAGS "sudy modnay" sudy1 -LDFLAGS "sim" sunday' 
| grep -oE '\-CFLAGS \".*\"' 
| sed 's/" /"\n/g' 
| sed 's/-CFLAGS/\n-CFLAGS/g' 
| grep "CFLAGS"

CodePudding user response:

A way to remove a pattern is to match it and replace it with nothing, globally

echo '...' | perl -wnlE's/-CFLAGS "[^"]*"//g; say'

With the string from the question this prints

version:  versions  sudy1 -LDFLAGS "sim" sunday

I use -l switch to remove a newline, so only the actual string is in $_ for further use. Or don't use -l and run under -p instead of -n so the final $_ gets printed.

To capture all these -CFLAGS "..." patterns

echo '...' | perl -wnlE'@m = /-CFLAGS "[^"]*"/g; say for @m'

(No need for that formidable pipeline from the question, indeed.)

To do both one can first run the match and then the substitution regexes shown above.

Or, one way to extract and remove at once is to run code in the replacement side

echo '...' 
| perl -wnlE's/(-CFLAGS "[^"]*")/push @m, $1; ""/ge; say; say for @m'

This prints both the final $_ and then the matches. Then to save the patterns in a file instead, one per line, one way

perl -MPath::Tiny -wnlE'
    s/(-CFLAGS "[^"]*")/push @m, $1; ""/ge; 
    say; 
    path("outfile.txt")->spew( join "\n, @m )'

(broken into lines for readability)   This uses the handy Path::Tiny.

I have the filename hard-coded as I'm unsure how the data is passed to the ("one-liner") program (really fed into STDIN via a pipe?), and how to pass a filename depends on that.


Another way to capture patterns and remove them would be to run code in the matching side, using the (?{code}) extended pattern; see it in perlretut and in in perlre (but in this case that has no advatanges over the above while it may add issues).

Yet another is to run the regex in a loop and add captures to an array one at a time, at each iteration, like push @m, $1 while /.../; add capturing parens around the pattern. This runs the engine multiple times (but what matters only with many matches or runs).

I find it curious that among the various predefined regex variables none keep all matches. (Efficiency? That would surely be useful to have, one of the more useful things I'd think.)


Or, we can open a file and write line by line, with no need for libraries

perl -wnlE'
    s/(-CFLAGS "[^"]*")/push @m, $1; ""/ge; 
    say; 
    open $fh, ">", "outfile.txt" or die $!;
    say $fh $_ for @m'

or even

perl -wnlE'
    open $fh, ">", "outfile.txt" or die $!;
    s/(-CFLAGS "[^"]*")/say $fh $1; ""/ge; 
    say'

This is perhaps slightly "riskier," for doing IO out of the regex.

  • Related