It's surprisingly hard to do that right. Not from the perl, but from the need to lock the file you're writing, and to avoid overwriting pieces of it.
I'm going to assume that the files are large enough that you won't want to slurp the whole thing into memory.
#!/usr/bin/perl
use warnings;
use strict;
use Fcntl qw/:DEFAULT :flock/;
my $fileA = '/path/to/file_a.txt';
my $fileB = '/path/to/file_b.txt';
my $fileC = '/path/to/file_a.bak';
my $tagline = qr/\QFile A: *** Initial Temperature/;
sysopen my $fc, $fileC, O_WRONLY | O_EXCL
or die 'Competing process: ', $!;
flock $fc, LOCK_EX | LOCK_NB
or die 'Competing process: ', $!;
open my $fa, '+<', $fileA or die $!;
flock $fa, LOCK_EX;
while (<$fa>) {
print $fc $_;
last if /^$tagline/;
}
open my $fb, '<', $fileB or unlink $fileC and die $!;
print $fc $_ while <$fb>;
close $fb or unlink $fileC and die $!;
print $fc $_ while (<$fa>);
close $fc or unlink $fileC and die $!;
# reverse the order of the next two statements if
# your system won't clobber the open file
rename $fileC, $fileA;
close $fa;
I use sysopen there to get the exclusive file open mode. If $fileC exists then there is an instance of your script working already (don't think it can't happen ;-). That's why I'm careful to delete $fileC before an error exit.
It's not easier to do if the files are small enough to slurp, just different. You'd then have the option of doing without the temporary file, but you'd have to seek to the beginning of $fa to write the new content. Inserting the new content would just be a string operation then, with lots of ways to do it.
Update: Modified the fail actions for opening and locking the temporary file. If there is a competing process or file, we want to stop and let the operator decide what to do.
|