How to replace a string in an existing file with Perl without touching unchanged files

Using

perl -pi -e 's/pattern/replacement/g' $(find src -type f) 

nice, with one exception: all files are overwritten, even those that have no match. This is not very good, as I often leave many of them open in Emacs or Eclipse, which then ask me boring questions. Is there an easy way to prevent touching immutable files (something like using grep in find is too much work, especially for complex templates).

+7
file perl
source share
3 answers

The very first -i after opening the file is unlink , so you cannot use -i for files that you do not want to modify.

 find src -type f -exec grep -Pl 'pattern' {} + | xargs perl -i -pe's/pattern/replacement/g' 

Of course, grep can already do recursive searches, so if you don’t need to use find to filter the results you specify later, you can use

 grep -Plr 'pattern' src | xargs perl -i -pe's/pattern/replacement/g' 

Note: cmd | xargs perl ... cmd | xargs perl ... can handle more files than perl ... $( cmd ) .

+4
source share

Instead of passing each file to perl to be processed, pre-select them.

Find files with pattern in them:

 grep -Plr 'pattern' src 

Then use the find call instead:

 perl -pi -e 's/pattern/replacement/g' $(grep -Plr pattern src) 

Or even like this:

 grep -Plr 'pattern' src | xargs perl -pi -e's/pattern/replacement/g' 

It is also likely to be faster because you are not processing files unnecessarily.

+4
source share

-p

causes Perl to take the following loop around your program, which causes it to iterate over the arguments in the file name somewhat like sed:

 LINE: while (<>) { ... # your program goes here } continue { print or die "-p destination: $!\n"; } 

-i [extension]

equivalently

 #!/usr/bin/perl $extension = '.orig'; LINE: while (<>) { if ($ARGV ne $oldargv) { if ($extension !~ /\*/) { $backup = $ARGV . $extension; } else { ($backup = $extension) =~ s/\*/$ARGV/g; } rename($ARGV, $backup); open(ARGVOUT, ">$ARGV"); select(ARGVOUT); $oldargv = $ARGV; } s/foo/bar/; } continue { print; # this prints to original filename } select(STDOUT); 

Now this means that you can take this skeleton script and change the backup restore if no replacements were made.

+2
source share

All Articles