Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Opening the same file over and over. Shorter way to do this?

by c (Hermit)
on Aug 01, 2001 at 17:53 UTC ( [id://101364]=perlquestion: print w/replies, xml ) Need Help??

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

In my script, i am touching a file to read, append and rewrite one right after the other. I am also locking the file each time I touch it and unlocking it once I am done with the read. I dont think this is a rarity in scripts, so I am assuming once again that not only is there more than one way to do it, but there is probably and more resourceful way of doing it.

sub add { open FH, ">>$file" or die "Cant open config file $!\n"; &lock(*FH); print FH "zone \"$formdata{domain}\" in \{\n\ttype slave\;\n\tfile\" +$client\/ db.$formdata{domain}\"\;\n\tallow-query \{ any\; \}\; \}\;\n\n"; &unlock(*FH); close FH; open FH, "$file" or die "Could not open $file : $!\n"; &lock(*FH); $/ = "\;\n\n"; my @array = <FH>; @array = sort @array; &unlock(*FH); close FH; open FH, ">$file"; &lock(*FH); print FH @array; &unlock(*FH); close FH; $/ = "\n"; }

I guess I have two main questions. First, do I need to lock and unlock the files with each open statement? or can I do this once at the beginning and then unlock at the end of the three procedures? I read over flock but didnt get a good feel of whether or not the statement could include multiple open and closes before an unlock statement was set.
And finally, does this process really need to have "$file" ">>$file" and ">$file"? I can't come up with other methods that work, so I offer it up for your advice.

humbly -c

Replies are listed 'Best First'.
Re: Opening the same file over and over. Shorter way to do this?
by Masem (Monsignor) on Aug 01, 2001 at 18:06 UTC
    I would use one temporary file, which needs not be locked, and then simply move the temporary file over to the old file. The only time you need to lock/unlock is when you are reading from the original file, and the only time you might encounter this lock is when you move the file over. Here's an example bit of code, but you'll have to fill in the blanks...
    sub add { open FH, "<$file" or die $!; &lock( *FH ); $/ = "\;\n\n"; my @array = <FH>; &unlock( *FH ); close FH; push @array, $my_long_domain_string_you_have; @array = sort @array; my $tempfile = "$file." . time(); # Unique id open FH, ">$tempfile" or die $!; &lock ( *FH ); print FH @array; &unlock( *FH ); close FH; # rename $file, "$file.old" or die $!; #Backup if desired rename $tempfile, $file or die $!; }

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain

Re: Opening the same file over and over. Shorter way to do this?
by suaveant (Parson) on Aug 01, 2001 at 18:27 UTC
    Here is a perl hint that has nothing to do with your question... your line: print FH "zone \"$formdata{domain}\" in \{\n\ttype slave\;\n\tfile\"$client\/db.$formdata{domain}\"\;\n\tallow-query \{ any\; \}\; \}\;\n\n"; could be much easier to read if you used qq print FH qq|zone "$formdata{domain}" in {\n\ttype slave;\n\tfile"$client/db.$formdata{domain}";\n\tallow-uery { any; }; };\n\n|; qq'' qq{} qq[] qq() qq-- any of those work... qq is the interpolating quote (you can use single q for non-interpolatin, like '') what it does is let you use an arbitrary character as your quote character, so you don't have to escape ". Or you can use matching braces {} [] or (). Also, you don't need to escape special perl characters like { and ; in a string.

                    - Ant

Re: Opening the same file over and over. Shorter way to do this?
by Cubes (Pilgrim) on Aug 01, 2001 at 18:17 UTC
    You might also want to take a look at seek, tell, and truncate. You can lock the file, open it once with +>> (read/write access in append mode, so you don't blow away whatever's already in there), then use these calls to do everything you need to do before closing and unlocking the file.
Re: Opening the same file over and over. Shorter way to do this?
by dga (Hermit) on Aug 02, 2001 at 01:16 UTC

    I like the implementation using rename. On UNIX/Linux rename is atomic which means you have no race condition.

    If you add sysopen with EXCL and CREAT on a temporary file with a fixed name then that call can only ever succeed if no one else is doing this update since O_EXCL | O_CREAT is atomic. If you can get all updaters, of which your program may be it, to use this convention you now have an atomic file open also. This eliminates the race condition in the original which would occur if someone grabs the lock and changes something between when you read and write. Now its locked for the entire critical section which allows only one updater to the file from the successful sysopen until the rename call. Readers will get either the entire old file or the entire new file with no chance of an inconsistent file. You could also choose to sleep a moment and retry the sysopen if you want an auto retry. Also you could have readers check for the temporary file also meaning they would wait until its gone to read but this has the problem of the updater dying leaving a lock effectively denying access to the old, yet consistent, file.

    I have also lexicalized the variables I used so as not to pollute the global name space. this may even compile under use strict; Also, I am using lexical variables for my file handles $in and $out. These will close automatically when the function returns but they should be closed before the rename happens. You will need a recent Perl to do this (5.6 works and maybe a little earlier versions also) And I did a local on the $/ assignment so that $/ is left alone when I return back to my caller.

    use Fcntl will get you the definitions of the O_ things. I am using write only (O_WRONLY) on the temp-file since I want to start at the top and go from there.

    Here is my code snippet implementing the method described here.

    use Fcntl; sub add { my($file, $domain_string)=@_; my $tempfile="Clever_but_constant_name_here"; my $in, $out; sysopen $out, "$tempfile", O_WRONLY|O_CREAT|O_EXCL or die "Tempora +ry file $tempfile exists"; open $in, "<$file" or die $!; local $/ = "\;\n\n"; my @array = <$in>; close $in; push @array, $domain_string; @array = sort @array; print $out @array; close $out; rename $tempfile, $file or die $!; }

    Enjoy.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://101364]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-10-12 14:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.