How to replace text in-place across files with perl -pi?
Question
How to replace text in-place across files with perl -pi?
Performing in-place text replacement across files with Perl’s -pi command-line flags is powerful, but using it inside a sandbox or script requires understanding how -p, -i, and -e work together.
Why the original snippet failed in the sandbox
The error syntax error at - line 1, near "pi -e " occurs because the flags -p and -i must be passed to the perl interpreter itself when called from the shell. When running Perl code directly (e.g. via perl - expecting a program on STDIN), you cannot embed -pi inside your code. Those are command-line options, not Perl code.
In other words, perl -pi -e 's/foo/bar/g' *.txt is a shell command; inside Perl itself, you must manually simulate what those flags do to edit files.
How to simulate perl -pi in a Perl script
The key is:
- Read each file line-by-line
- Do the substitution on each line
- Write back to the same file, optionally saving a backup first
This can be done in a simple script that you run as perl - (feeding the script through STDIN). Here’s a complete example that replaces foo with bar in two sample files, mimicking perl -pi.bak -e 's/foo/bar/g':
use strict;
use warnings;
# Sample files to edit
my @files = ('example1.txt', 'example2.txt');
# Create sample files with content including 'foo'
for my $file (@files) {
open my $fh, '>', $file or die "Cannot write $file: $!";
print $fh "foo is here\nanother foo line\n";
close $fh;
}
for my $file (@files) {
# Backup original file
rename $file, "$file.bak" or die "Cannot backup $file: $!";
open my $in, '<', "$file.bak" or die $!;
open my $out, '>', $file or die $!;
while (<$in>) {
s/foo/bar/g; # Substitute foo with bar
print $out $_;
}
close $in;
close $out;
}
# Print modified file contents
for my $file (@files) {
print "Contents of $file after substitution:\n";
open my $fh, '<', $file or die $!;
print while <$fh>;
close $fh;
}
Key Perl Concepts Illustrated
@files: Array holding filenames — sigils like@indicate arrays.open: Opens a filehandle for reading (<) or writing (>).while (<$in>): Reads line-by-line from input filehandle.s/foo/bar/g;: Regular expression substitution (all occurrences in the line).rename: Used to make a backup before editing (like-i.bak).
This manual approach is what perl -pi shorthand handles internally.
Common pitfalls when using -pi in command line
- Make sure you run
perl -pi -e 's/foo/bar/g' filesfrom a shell, not in a Perl script. - Escaping issues: In shells like Bash, use single quotes to protect Perl code, but on Windows CMD you may need double quotes.
- Backup extension:
-i.bakcreates backups;-ialone edits without backups—be careful! - Binary files: line-by-line edits may corrupt binary data.
Summary
To replace text in-place across files from a shell, use:
perl -pi.bak -e 's/foo/bar/g' *.txt
To emulate that behavior inside a Perl script (runnable with perl -), read and rewrite files line-by-line making backups yourself, as shown above.
Verified Code
Executed in a sandbox to capture real output. • v5.34.1 • 12ms
Contents of example1.txt after substitution:
bar is here
another bar line
Contents of example2.txt after substitution:
bar is here
another bar line
(empty)