Perl newbie Perl newbie - 2 years ago 105
Perl Question

PERL:To write in input file (without overwrite the original file)

Below is my input file as well as acts my output file too. Need assistance to read and write in the input file. (PS: Input and output is same file)

TS_dunit_ PDX_VER_6
TS_test1_par PDX_VER_0

my code look likes below;


use Getopt::Long;
use strict;
use warnings;

my $file;

"iofile=s" => \$file
if (not defined $file){
print "Please specify input_output (iofile) file\n";

open (my $fh, "$file") or die "Can't open the file $file: ";
open (my $fh1, ">>$file") or die "Can't open the file $file: ";

while (<$fh>){
chomp $_;
next if ($_ !~ /S+/);
$_ =~ /(\S+)\s+(\S+)/;
my $first_underscore =index ($1, '_');
my $dev = substr ($1, $first_underscore + 1,
rindex ($1, '_') - $first_underscore - 1);
my $tag = $2;
my $cat_path = "/testdata/17.26.6/$dev/sd/$tag";
my $arc_path = "archive/$dev/sd/$tag";
if (-d $cat_path){
print $fh1 "$dev $tag IN_CAD\n";
}elsif (-d $arc_path){
print $fh1 "$dev $tag IN_ARCHIVE\n";
print $fh1 "NA\n";

print "Done! File been append.\n";

Above code gives output as

TS_dunit_ PDX_VER_6
TS_test1_par PDX_VER_0


need help if anyway i can make the output as below instead.

TS_dunit_ PDX_VER_6 IN_CAD
TS_test1_par PDX_VER_0 IN_CAD

Answer Source

One cannot append to a line in a file without overwriting the rest of the file. A file is a sequence of bytes and we can't "insert" characters between the existing ones. See this post for more detail, for instance.

Instead, write out a new file and then rename it to the original. This changes the inode number; if you need to keep it see the end. The code simplifies the index+substr part, by regex.

use warnings;
use strict;
use feature 'say';
use File::Copy qw(mv);

# ... code from the question 

open my $fh,     '<', $file    or die "Can't open $file:$!";
open my $fh_out, '>', $outfile or die "Can't open $outfile:$!";

while (<$fh>) 
    next if not /\S/;

    my ($dev, $tag) = /.*?_(.*)_\s+(.*)/;

    my $cat_path = "/testdata/17.26.6/$dev/sd/$tag";
    my $arc_path = "archive/$dev/sd/$tag";

    if (-d $cat_path) {
        say $fh_out "$_ IN_CAD";
    elsif (-d $arc_path) {
        say $fh_out "$_ IN_ARCHIVE";
    else {
        say $fh_out "NA";
close $fh;
close $fh_out;

# Changes inode number. See text for comment
move($fh_out, $fh) or die "Can't move $fh_out to $fh: $!";

The regex matches up to the first _, since ? makes .*? non-greedy (it stops at first _). Then it captures everything until the last _ since .* is greedy, matching everything until the very last _. This is what the code in the question does, by using rindex. Then it captures all after tabs/spaces.

The current line is then printed appended, as in the question, to the output file. Since the output file is temporary its name should be built using File::Temp. Then the file is renamed using File::Copy.

This changes the inode number. If that matters, one way to keep the inode number is as follows. After the output file has been written out open the original file for writing, which will truncate it. Then read from the output file and write to the original file. The content gets copied, to the same inode. Remove the output file when done.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download