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

Unable to write into the .sv file extension but I am able to read and write the .txt file extension
open(FILE, "<generic_bist_ctrl_defines.sv") || die "File not found"; my @lines = <FILE>; close(FILE); foreach(@lines) { $_ =~ s/=131/= $a/g; } open(FILE, ">generic_bist_ctrl_defines.sv") || die "File not found"; print FILE @lines; close(FILE); close $in;

Replies are listed 'Best First'.
Re: Unable to write the .sv file
by marto (Cardinal) on Mar 12, 2021 at 15:23 UTC

    Use the three argument open, and let it tell you why it failed:

    open(my $fh, '>', 'generic_bist_ctrl_defines.sv') or die "Can't open > generic_bist_ctrl_defines.sv: $!";

      (++) Also DRY on the filename because a typo in just the first one is an intensely annoying bug when it happens.

      my $outfile = 'generic_bist_ctrl_defines.sv'; open my $fh, '>', $outfile or die "Can't open > $outfile: $!";

      🦛

Re: Unable to write the .sv file
by davido (Cardinal) on Mar 12, 2021 at 18:42 UTC

    Your error handling is not revealing the actual error message. Consider this code:

    || die "File not found";

    Are you sure that's why open failed? There are reasons other than the file not being found, and you aren't actually displaying the message that the system call produces. Your "die" statements should also output the contents of $!, which is described in perlvar.

    || die "Unable to open input file: $!"; # ... and ... || die "Unable to open output file: $!";

    By displaying the actual error message instead of the one you assume happened, you learn a lot more about why something failed.

    If I were writing it, the code would look more like this:

    use strict; use warnings; use File::Temp; use File::Copy qw(move); use Fcntl qw(:flock); my $target_file = 'generic_bist_ctrl_defines.sv'; { open my $in_fh, '<', $target_file or die "Cannot open input file: +$!\n"; flock $in_fh, LOCK_EX | LOCK_NB or die "Cannot obtain a lock on $t +arget_file: $!\n"; my $temp_out = File::Temp->new(TEMPLATE => "$0-$$-XXXXX"); while (my $line = <$in_fh>) { $line =~ s/=131/= $a/g; # Did you actually mean $line =~ s/=(1 +31)/= $1/g; ??? print $temp_out $line; } close $in_fh or die "Failed to close $target_file. Aborting. $!\n" +; $temp_out->flush; eval { move($temp_out->filename, $target_file); } or do { warn "Failed to swap $temp_out into $target_file: $!\n"; # There may need to be more cleanup here, or possibly a die is + more appropriate. }; } # $temp_out falls out of scope; temp file should be removed even i +f the move call failed. # flock falls out of scope; lock is released on $target_file.

    And I'd want to test it to make sure I hadn't missed something, since File::Temp can be tempermental if used incorrectly, and because I would want to verify I'm using File::Copy's move correctly. But the pattern is approximately correct.

    The pattern here is:

    1. Open an input file and verify it opened correctly.
    2. Lock the input file with an exclusive lock, and hope that other processes that access the file also use locking appropriately.
    3. Open a tempfile.
    4. Read the input file line by line (avoids slurping into memory)
    5. Alter each line as needed.
    6. Write each line to the tempfile.
    7. When the input file is done being read, close it. Verify the close because we would not want to proceed to clobber the input file if the close failed (we may not have gotten all the contents).
    8. Move the tempfile over the original target file. If the move fails, warn or throw with a descriptive error message.
    9. Close the scope on the input and tempfile filehandles so that the lock expires on the input file, and the tempfile is cleaned up. Scope would also close in a 'die', so whether a move failure warns or dies, the tempfile should get cleaned up either way.

    The reason for writing to a tempfile and moving is to reduce the potential for corruption of the input file, by making its replacement an atomic event.

    Alternatively, you could use a module such as Tie::File, which is a fairly safe way to work line-by-line through files, modifying as you go. But it's less efficient. For small files or infrequent writes, it probably doesn't matter. For large files or frequent rewrites, it's probably not optimal. Here's an example:

    use strict; use warnings; use Fcntl qw(:flock); use Tie::File; my $target_file = '.....'; { my ($tied, @file_content) $tied = tie @file_content, 'Tie::File', filename or die "Unable to + tie $target_file: $!\n"; $tied->flock(LOCK_EX|LOCK_NB) or die "Cannot lock $target_file. Ab +orting. $!\n"; foreach my $line (@file_content) { $line =~ s/foo/bar/; } } # $tied falls out of scope. File is closed, lock expires.

    It's simpler code, just less efficient. And it does allow you to avoid meddling with tempfiles yourself. However, File::Temp doesn't create a single atomic write; it writes changes as you make them line by line. So you could become exposed to corruption if someone else is messing with the file (ignoring advisory locks). If that's a possibility, you're much better off going with some version of the first solution.


    Dave

      Thanks. It is working fine.
Re: Unable to write the .sv file
by choroba (Cardinal) on Mar 12, 2021 at 14:45 UTC
    Please, use the <code>...</code> tags to make your post readable.

    What do you mean by "unable to write"? Do you see any errors? Hasn't the file changed? Or are the changes unexpected?

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Unable to write the .sv file
by BillKSmith (Monsignor) on Mar 12, 2021 at 15:56 UTC
    Do you have write permission on the .txt file but not the .sv file?
    Bill
      I have the write, read and execute permission in both files