0

I am having the darnedest time with a bug. I recently asked Perl Regular Expression for extracting multi-line LaTeX chapter name, and followed the advice to use a Perl regular expression tester until I'm satisfied with my regular expression. I committed my code and was happy.

But now, when I run the program:

#!/usr/bin/perl -i.old     # In-place edit, backup as '.old'
use strict;
use warnings;

use Path::Tiny;

my $filename = shift or die "Usage: $0 FILENAME";
my $content = path($filename)->slurp_utf8;

$content =~ s/chapter/addchap/g;

path($filename)->spew_utf8($content);

I end up with an empty file. It is very strange -- variations of this substitution (like s/\\chapter/\\addchap/g;) fail with an empty file, but dissimilar substitutions, like $content =~ s/ //g; don't. Even stranger, the perl -i flag isn't being respected either. What the heck is going on?

Here is some sample input:

\chapter{\texorpdfstring{{I} {Into the
Primitive}}{I Into the Primitive}}

Buck did not read the newspapers, or he would have known that trouble
was brewing, not alone for himself,

I updated my code, per the answers, but I am still getting empty files.

#!/usr/bin/perl
use strict;
use warnings;

use Path::Tiny;

my $filename = shift or die "Usage: $0 FILENAME";
my $content = path($filename)->slurp_utf8;

$content =~ s/\\chapter/\\addchap/gs;

path($filename)->spew_utf8($content);

The thing I find really confusing is that some substitutions "work", like:

$content =~ s/the/thee/g;

but even

$content =~ s/ch//g;

fails with an empty file.

nomen
  • 3,626
  • 2
  • 23
  • 40

2 Answers2

3

You don't want that -i flag and Path::Tiny at the same time. -i is putting perl into a mode where it's trying to open and replace the file for you. It's not compatible with opening the file and rewriting it yourself.

One option is simply to remove the -i flag; your code should work as-is (but it won't produce a backup file, and the file replace won't be atomic).

The other is to keep the -i flag, but replace the body of your code with

use open ':encoding(UTF-8)';
while (<>) {
    s/chapter/addchap/g;
    print;
}

(eliminating all of the Path::Tiny stuff). Perl will add the rest implicitly.

This is equivalent to the one-liner

perl -CSD -i.old -pe 's/chapter/addchap/g' somefile
hobbs
  • 223,387
  • 19
  • 210
  • 288
1

The -i command line option on the first line of the script is probably causing this problem. Try deleting that.

When invoked with perl -i, Perl wraps some code around your script to loop over each file named on the command-line; read each line into $_ in turn; invoke your script (once for each line); collect whatever output your script prints to STDOUT and write it into a temporary file; then after the last line, rename the temporary file over the top of the original file. This is called an "in-place edit".

This is particularly handy for 1-liners, because you don't need to write the boilerplate code to read the input from a file and write the output to a file - you just write the processing code. In your case, you have written that boilerplate (using Path::Tiny) so both Perl's -i implementation and your script are reading the input file and writing the output file. Since your script does not output anything to STDOUT, -i will end up producing an empty file.

Grant McLean
  • 6,898
  • 1
  • 21
  • 37