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

Hi experts, I came up with the following way to change a line in a file. The file size is always <5,000 bytes. I do get an error message as follows:
readline() on closed filehandle T at blah. See comment below for line. + print() on closed filehandle T at blah. See comment below for line.
Need I say, it looks right to me... I've looked at many examples. The subroutine originally just wrote the file. I am testing a way to change a line that starts with 'JOB=' to "RMT=filename. Thanks for your help and warm regards.
sub t_write { my($file, $aOrder) = @_; my($trace) = $aOrder->{trace_file_data}; my $counter = 0; if (!defined($trace) || length($trace) == 0) { print "No trace file.\n"; return; } open(F, "> $file\0") or die "Can't create trace-file $file ($!)\n" +; print F $trace; close(F); # new start testing >>>>>> open (T, "+<$trace"); my @array = <T>; #this has the readline error foreach my $line (@array) { if ($line =~ /^JOB=/) { $array[$counter] = "RMT=$file\n"; } $counter++; } print T @array; #this has the print() error close(T); # end testing <<<<< #make a comment that this was done print "Trace file: $file\n"; }

Replies are listed 'Best First'.
Re: Change a line in a file
by betterworld (Curate) on Aug 18, 2008 at 02:36 UTC
    1. open appears to fail. Always check whether open succeeds: open T, "+<$trace" or die "$trace: $!";

      When writing to a file, checking for errors from print and close can't harm either. You want to be informed if your disk has not enough space for the data.

    2. You'd probably have to seek to the beginning of the file and truncate it before writing after reading. But if you want to be prepared for crashes between "truncate" and "print" and don't want to lose your data, you'd better write to a temporary file and then move it over the old file with rename.

    3. Maybe Tie::File helps you solve your problem faster.

Re: Change a line in a file
by GrandFather (Saint) on Aug 18, 2008 at 02:38 UTC

    Try putting a or die ... test on open (T, "+<$trace"). Are you perhaps trying to read and write the same file as you have open elsewhere?

    It's generally recommended to use the three parameter version of open - it's easier to read and safer.


    Perl reduces RSI - it saves typing
Re: Change a line in a file
by BrowserUk (Patriarch) on Aug 18, 2008 at 02:48 UTC
Re: Change a line in a file
by CountZero (Bishop) on Aug 18, 2008 at 05:48 UTC
    As $line is an alias for the subsequent elements in the array, you can replace
    foreach my $line (@array) { if ($line =~ /^JOB=/) { $array[$counter] = "RMT=$file\n"; } $counter++; }
    by
    foreach my $line (@array) { $line =~ s/^JOB=/RMT=$file\n/; }
    or even shorter by
    s/^JOB=/RMT=$file\n/ for @array;
    • If the entry "JOB=" already contains a new-line, you can drop the "\n" in the substitution.
    • If there is data after the "JOB=", the pattern to search for should be "JOB=.*" so you match and replace all of the line.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Thank you CountZero. This gave me the best solution. Turns out I was trying to open the file incorrectly as "netbpa" astutely noticed but in the end I did not have to open the file the second time. I took your shortest suggestion and used it on the string $trace as follows:
      sub t_write { my($file, $aOrder) = @_; my($trace) = $aOrder->{trace_file_data}; if (!defined($trace) || length($trace) == 0) { print "No trace file.\n"; return; } $trace =~ s/JOB=.*/RMT=$file/; open(F, "> $file\0") or die "Can't create trace-file $file ($!)\n" +; print F $trace; close(F); #make a comment that this was done print "Trace file: $file\n"; }
      This solution added only one line to the original subroutine! Thank you. Warm regards,
Re: Change a line in a file
by netbpa (Initiate) on Aug 18, 2008 at 23:40 UTC
    It looks to me like you've mixed up the usage of $file and $trace. $trace looks like it contains the contents, but you are using it as the filename passed to open just after 'now start testing >>>>'. Previously, you used $file.