pret_23_1 has asked for the wisdom of the Perl Monks concerning the following question:

I'm new to perl I could not fing the problem inthis code test.cfg
newyork 1536000 13 56 california 1536000 67 45 London 456 newyork 1536000 78 87
open FILE, "+< test.cfg" || die "Cannot open file\n" ; while ($line = <FILE>) { if ($line =~ /newyork/ ) { $line =~ s/1536000/7878787878/; print " the line is $line"; } } close FILE;
when i print the line the value is changed but when i vi the file it still has the old value..any help of what's going wrong ..

Replies are listed 'Best First'.
Re: reg perl substitution
by moritz (Cardinal) on Nov 09, 2007 at 17:57 UTC
    You read lines from a file into a variable, and then change that variable - you don't change the file.

    There are several options, perhaps the easiest is to write to a temporary file first, and then move this to the location of the old file.

    Or you could use Tie::File - that changes the file that it's tied to.

Re: reg perl substitution
by tuxz0r (Pilgrim) on Nov 09, 2007 at 19:22 UTC
    Tie::File is a nice way to go; but, is this all your code needs to do? Is there more to this script (it would help if you used <code> tags around the script in your post)? If this is all you have to do, you might consider using the -i option to modify the file in place and do it in a one-liner,
    $ perl -i.bak -ape '/^newyork/ and s/153600/7878787878/' test.cfg
    I assume you want to match the first occurence of 'newyork' (there's two in the line), so I anchored the pattern. Modify to suit your needs. The '-i' will save the original file with a .bak extension and modify the file in place for you.

    ---
    echo S 1 [ Y V U | perl -ane 'print reverse map { $_ = chr(ord($_)-1) } @F;'
    Warning: Any code posted by tuxz0r is untested, unless otherwise stated, and is used at your own risk.

Re: reg perl substitution
by mwah (Hermit) on Nov 09, 2007 at 19:17 UTC
    hen i print the line the value is changed but when i vi the file it still has the old value..any help of what's going wrong

    As moritz said, you are just "reading" a file. Thats it. BUT: Perl provides a special way to modify lines of files via its command line interface. The following command would read the file line by line and change the sequence "newyork 1536000" to the desired output - which is written to a temporary file and renamed to the original file name afterwards. The original file will be kept and named test.cfg.bak (this is what the -i option does).

    Unix/Linux: perl -i.bak -pe 's/(newyork\s+)1536000/${1}7878787878/' test.cfg Windows: perl -i.bak -pe "s/(newyork\s+)1536000/${1}7878787878/" test.cfg

    Regards

    mwa

Re: reg perl substitution
by ikegami (Patriarch) on Nov 10, 2007 at 02:38 UTC

    Hopefully the others have addressed your issue. I'd like to comment on

    open FILE, "+< test.cfg" || die "Cannot open file\n" ;

    It's the same thing as

    open FILE, ("+< test.cfg" || die "Cannot open file\n") ;

    You want

    open FILE, "+< test.cfg" or die "Cannot open file\n";

    General rule: If you don't care about the return vakue of the OR, you want or instead of ||.

    Better yet, use a lexical file handle, use the 3-arg form of open, and include the cause of the error ($!) in the error message.

    open my $fh, '+<', 'test.cfg" or die "Cannot open file: $!\n";
Re: reg perl substitution
by Anonymous Monk on Nov 09, 2007 at 19:28 UTC
    Hi thanks all for the help. This is a small part of a bigger module . I think i have to use tie::file.I went thru the perl doc ..but its not clear how to use it .could you give me a simple eg how to use it .. Thanks again fo rthe help
      Tie::File may suit your problem but I think there was a thread or something in the CB a few days ago to the effect that every change to the array tied to your file results in a complete file write as writes in Tie::File can't be deferred. This may cause performance problems if your module is modifying many lines in a large file.

      Here is a simple example using your data.

      # cat test.cfg newyork 1536000 13 56 california 1536000 67 45 London 456 newyork 1536000 78 87 $ cat spw649958 #!/usr/bin/perl # use strict; use warnings; use Tie::File; my $file = q{test.cfg}; tie my @lines, q{Tie::File}, $file or die qq{tie: $file: $!\n}; foreach my $line ( @lines ) { next unless $line =~ m{^newyork}; $line =~ s{1536000}{7878787878}; } untie @lines or die qq{untie: $file: $!\n}; $ ./spw649958 $ cat test.cfg newyork 7878787878 13 56 california 1536000 67 45 London 456 newyork 7878787878 78 87 $

      I hope this is of use.

      Cheers,

      JohnGG

      think i have to use tie::file.I went thru the perl doc ..but its not clear how to use it .could you give me a simple eg how to use it

      for Tie::File, you have look at the file's lines as if they would be like "array elements", - by changing them you'd change the file. What did you try so far - what didn't work?

      BTW, this problem looks rather simple, so you could employ your own file handling - sth. like this:

      ... my ($fnNew, $fnOld) = ('test.cfg', 'test.cfg.bak'); rename $fnNew, $fnOld or die "can't rename $fnNew, $!"; open my $fin, '<', $fnOld || die "$fnOld - $!"; open my $fout, '>', $fnNew || die "$fnNew - $!"; while( my $line = <$fin> ) { $line =~ s/(newyork\s+)1536000/${1}7878787878/; print $fout $line } close $fin; close $fout; ...

      This would do basically the same as the 'one-liner' in another example already given.

      Regards

      mwa