jdesilvio
jdesilvio

Reputation: 1854

Replace all matches EXCEPT first match in multiple files with perl

I need to do a find/replace in multiple files but I want to keep the first occurrence of the match and replace all others. THis may have been answered else where but it's a bit difficult to search for.

I am currently using: perl -pi -w -e 's/THING/SOMETHING_ELSE/g;' ./src/**/*.py

A practical example:

from stuff import THING

print(f"use {THING} here")
print(f"use {THING} there")
print(f"use {THING} everywhere")

Should become:

from stuff import THING

print(f"use {SOMETHING_ELSE} here")
print(f"use {SOMETHING_ELSE} there")
print(f"use {SOMETHING_ELSE} everywhere")

Upvotes: 1

Views: 47

Answers (2)

brian d foy
brian d foy

Reputation: 132822

It sounds like you want to skip the from line, so I'd just skip those. This expression either matches that from or tries to do the substitution. That is, the substitution is not attempted on any line that starts with from:

$ perl -pi -w -e 'm/^from\s/ or s/\bTHING\b/SOMETHING_ELSE/g;' test.py
from stuff import THING

print(f"use {SOMETHING_ELSE} here")
print(f"use {SOMETHING_ELSE} there")
print(f"use {SOMETHING_ELSE} everywhere")

This skips every from line, so if you wanted to change other from lines, this isn't going to work.

Notice that I also use the \b word anchor boundaries around THING. Otherwise, you get what I got I ran the program again: SOMESOMETHING_ELSE_ELSE :)

There are various other ways to accomplish this task but they aren't so nice as one-liners.

Upvotes: 0

Andy Lester
Andy Lester

Reputation: 93676

You'll have to check for /THING/ before doing the s/THING/.../.

Here's how would be as a script thing-to-other that you can then call as thing-to-other ./src/**/*.py

#!/bin/env perl -i -p

if ( /THING/ ) {
    if ( $seen{$ARGV}++ ) {
        s/THING/SOMETHING_ELSE/g;
    }
}

Note that I got rid of the -w because the use of the %seen hash will throw a warning. The %seen hash keeps track how many times in each input file ($ARGV) you've seen /MATCH/.

If you really need it to be a one-liner:

perl -i -p -e'if (/THING/) {if($seen{$ARGV}++){s/THING/SOMETHING_ELSE/g}}'

Upvotes: 1

Related Questions