87

I want to replace the word "blue" with "red" in all text files named as 1_classification.dat, 2_classification.dat and so on. I want to edit the same file so I tried the following code, but it does not work. Where am I going wrong?

@files = glob("*_classification.dat");
foreach my $file (@files)
{
    open(IN,$file) or die $!;
    <IN>;
    while(<IN>)
    {
        $_ = '~s/blue/red/g';
        print IN $file;
    }
    close(IN)
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user831579
  • 981
  • 1
  • 8
  • 8

4 Answers4

180

Use a one-liner:

$ perl -pi.bak -e 's/blue/red/g' *_classification.dat

Explanation

  • -p processes, then prints <> line by line
  • -i activates in-place editing. Files are backed up using the .bak extension
  • The regex substitution acts on the implicit variable, which are the contents of the file, line-by-line
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Zaid
  • 36,680
  • 16
  • 86
  • 155
  • Yeah, or no quotes at all, if the code doesn't contain spaces. – bart Aug 09 '11 at 10:55
  • 6
    Using the `*` globbing in arguments does not seem to work in windows. – TLP Aug 09 '11 at 12:01
  • 2
    I notied that on windows I had to used double quotes around the regex – Tan Rezaei Apr 15 '16 at 00:31
  • `glob` hack to support wildcard command line arguments (`*.dat`) for Windows users: `BEGIN { @ARGV = map +glob, @ARGV }` – Zaid Nov 20 '19 at 16:27
  • What if search and replacement strings are in variables? `perl -pi -e 's/$oldKey/$trimmedNewKey/g' AppConstants.txt`, did not work for me – nr5 Jan 11 '20 at 19:54
  • @nr5, Look at my answer below. I had a case it didn't work for me either and the issue was the Regular Expression processing, – Royi May 02 '20 at 09:05
  • How do I do substitution atomically (per-file, not per-line) without any backup files? Should i use -i or not? I don't understand what "in-place editing" means. I want to do substitution in files that I'm watching for changes, so it needs to be atomic. – geekley Dec 08 '20 at 03:00
  • @nr5 The problem is that bash variables don't expand within single quoted strings. Use double quotes. – drevicko Jun 03 '22 at 17:26
22

None of the existing answers here have provided a complete example of how to do this from within a script (not a one-liner). Here is what I did:

rename($file, $file . '.bak');
open(IN, '<' . $file . '.bak') or die $!;
open(OUT, '>' . $file) or die $!;
while(<IN>)
{
    $_ =~ s/blue/red/g;
    print OUT $_;
}
close(IN);
close(OUT);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cmbryan
  • 395
  • 2
  • 9
14

$_='~s/blue/red/g';

Uh, what??

Just

s/blue/red/g;

or, if you insist on using a variable (which is not necessary when using $_, but I just want to show the right syntax):

$_ =~ s/blue/red/g;
bart
  • 7,640
  • 3
  • 33
  • 40
2

It can be done using a single line:

perl -pi.back -e 's/oldString/newString/g;' inputFileName

Pay attention that oldString is processed as a Regular Expression.
In case the string contains any of {}[]()^$.|*+? (The special characters for Regular Expression syntax) make sure to escape them unless you want it to be processed as a regular expression.
Escaping it is done by \, so \[.

Royi
  • 4,640
  • 6
  • 46
  • 64