简体   繁体   中英

How to replace string in a file with Perl in script (not in command line)

I want to replace a string in a file. Of course I can use

 perl -pi -e 's/pattern/replacement/g' file

but I want to do it with a script.

Is there any other way to do that instead of system("perl -pi -es/pattern/replacement/g' file") ?

-i takes advantage that you can still read an unlinked filehandle, you can see the code it uses in perlrun . Do the same thing yourself.

use strict;
use warnings;
use autodie;

sub rewrite_file {
    my $file = shift;

    # You can still read from $in after the unlink, the underlying
    # data in $file will remain until the filehandle is closed.
    # The unlink ensures $in and $out will point at different data.
    open my $in, "<", $file;
    unlink $file;

    # This creates a new file with the same name but points at
    # different data.
    open my $out, ">", $file;

    return ($in, $out);
}

my($in, $out) = rewrite_file($in, $out);

# Read from $in, write to $out as normal.
while(my $line = <$in>) {
    $line =~ s/foo/bar/g;
    print $out $line;
}

You can duplicate what Perl does with the -i switch easily enough.

{
    local ($^I, @ARGV) = ("", 'file');
    while (<>) { s/foo/bar/; print; }
}

You can try the below simple method. See if it suits your requirement best.

use strict;
use warnings;

# Get file to process
my ($file, $pattern, $replacement) = @ARGV;

# Read file
open my $FH, "<", $file or die "Unable to open $file for read exited $? $!";
chomp (my @lines = <$FH>);
close $FH;

# Parse and replace text in same file
open $FH, ">", $file or die "Unable to open $file for write exited $? $!";
for (@lines){
    print {$FH} $_ if (s/$pattern/$replacement/g);
}
close $FH;

1;

file.txt:

Hi Java, This is Java Programming.

Execution:

D:\swadhi\perl>perl module.pl file.txt Java Source

file.txt

Hi Source, This is Source Programming.

You can use

sed 's/pattern/replacement/g' file > /tmp/file$$ && mv /tmp/file$$ file

Some sed versions support the -i command, so you won't need a tmpfile. The -i option will make the temp file and move for you, basicly it is the same solution.

Another solution (Solaris/AIX) can be using a here construction in combination with vi:

vi file 2>&1 >/dev/null <@
1,$ s/pattern/replacement/g
:wq
@

I do not like the vi solution. When your pattern has a / or another special character, it will be hard debugging what went wrong. When replacement is given by a shell variable, you might want to check the contents first.

You can handle the use case in the question without recreating the -i flag's functionality or creating throwaway variables. Add the flag to the shebang of a Perl script and read STDIN:

#!/usr/bin/env perl -i

while (<>) {
    s/pattern/replacement/g;
    print;
}

Usage: save the script, make it executable (with chmod +x ), and run

path/to/the/regex-script test.txt

(or regex-script test.txt if the script is saved to a directory in your $PATH.)


Going beyond the question:

If you need to run multiple sequential replacements, that's

#!/usr/bin/env perl -i

while (<>) {
    s/pattern/replacement/g;
    s/pattern2/replacement2/g;
    print;
}

As in the question's example, the source file will not be backed up. Exactly like in an -e oneliner, you can back up to file.<backupExtension> by adding a backupExtension to the -i flag. For example,

#!/usr/bin/env perl -i.bak

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM