0

I embedded one perl one-liner in one perl script , the purpose of which is to in-place replace one string with another for each line of the files with name extension .ini, but I found that every file is backed up with .bak.

Since in the one-liner, I only specify -i rather than -i.bak, I wonder why the .bak file could be generated.

foreach my $dir (glob "*.ini") {
    system qq(perl -i -pe 's|Default|UTF-8|' $dir);          
}

Another question is whether there is any better,concise perl script to achieve the same target (without .bak file to be backed up), regardless of one-liner or not.

Zii
  • 359
  • 4
  • 10

4 Answers4

2

I think you may be looking at some old data files. As far as I know, Perl won't assume a default file extension of .bak under any circumstances

If you specify the option as just -i then it will attempt to edit the file in-place by deleting the input file after it has been opened and creating a new output file with the same name. The old file remains readable until it is closed

This technique doesn't work on Windows, and so it is an error to use a bare -i option on that platform

There is no need to shell out to a second perl process just to edit some files. It can be done very simply like this. The internal variable $^I corresponds to the value of the -i option on the command line. Once again, if you are working on a Windows system then $^I may not be an empty string and you will need to set it to .bak or something similar

{
    local @ARGV = glob 'f.*';
    local $^I = '';  # Must be non-blank on Windows

    while ( <> ) {
        s/Default/UTF-8/g;
        print;
    }
}
Borodin
  • 126,100
  • 9
  • 70
  • 144
2

perl -i boils down to the following core:

open(my $fh_in, '<', $qfn);
unlink($qfn);
open(my $fh_out, '>', $qfn);

$fh_in is called an anonymous file handle. Notice how the associated file from which it reads no longer exists in the directory.

The problem is that Windows doesn't support anonymous file handles. When you delete a file that's already open, the directory entry remains until the file is closed. This prevents Perl from creating an output file with the same name. That's why -i (without an extension) isn't supported on Windows.

>perl -i -ne"..." file
Can't do inplace edit without backup.

But you aren't using Windows. You're using cygwin, an environment that emulates unix. That's why using -i (without an extension) didn't give you an error. However, cygwin is still limited to the capabilities of the host OS, so it can't create anonymous files[1]. As such, Perl is programmed to behave as if -i.bak was provided when -i is provided to a cygwin build of Perl.


  1. It seems to be able to emulate them, but that feature isn't being used for some reason.

    $ cat foo
    abc
    def
    
    $ perl -E'
       open(my $fh, "<", "foo") or die $!;
       system("ls -1");
       say "--";
       unlink("foo") or warn $!;
       system("ls -1");
       say "--";
       print while <$fh>;
    '
    foo
    --
    --
    abc
    def
    
ikegami
  • 367,544
  • 15
  • 269
  • 518
1

Here's a way to do it without having to shell-out another instance of perl. This technique uses an intermediary temp file:

use warnings;
use strict;

for my $file (glob "*.ini") {
    open my $fh, '<', $file or die $!;
    open my $wfh, '>', 'temp.txt' or die $!;

    while (<$fh>){
        s/Default/UTF-8/;
        print $wfh $_;
    }
    close $fh;
    close $wfh;
    unlink $file or die $!;
    rename 'temp.txt', $file;
}

Use this version if you know that your files are not overly large. It reads the whole file into memory (a single string scalar), and doesn't require the intermediary temp file:

use warnings;
use strict;

for my $file (glob "*.ini") {
    open my $fh, '<', $file or die $!;

    my $contents;
    {
        local $/;
        $contents = <$fh>;
    }
    close $fh;

    $contents =~ s/Default/UTF-8/;

    open my $wfh, '>', $file or die $!;
    print $wfh $contents;
    close $wfh;
}
stevieb
  • 9,065
  • 3
  • 26
  • 36
0

I think bash command find + xargs and perl should be easier than perl loop.

find . -name '*.ini' |xargs perl -pi -e 's/Default/UTF-8/g'
laojianke
  • 44
  • 4