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

Hi Monks ,

Need your help .

I am facing problem when creating a directory using mkdir command in perl .

we have jobs to create directory on daily basis with date stamp and place some files in it based on some condition , so when new day begins our script will create a directory with the current date stamp .

( for example direcotry name looks like" /<dir>/<dir1>/2011-04-02 " )

Issue is when we ran the same script twice at same time on new day , say like both scripts are started around Apr 2 00:01:31 UTC , one job got failed with reason " Cannot create directory".

i used unless ( -d $dir ) { } option to check the presence of the directory , but its not working in this case .

next jobs ran fine because of the " unless ( -d $dir ) { } " conditions , but the (two same jobs at same time ) jobs which are trying to create the file is giving the problem .

is there any other option to avoid this . Please help me on this .

Replies are listed 'Best First'.
Re: Directory creation in Perl using mkdir
by moritz (Cardinal) on Apr 04, 2011 at 08:02 UTC
    You can try to call mkdir first, then check for the existence:
    my $error; mkdir $path or $error = $!; unless (-d $path) { die "Cannot create directory '$path': $error"; }

    That should avoid race conditions in directory creation.

      Why do you need the variable $error? Can't you simply say:

      mkdir $path; die "Cannot create directory $path : $!\n" unless -d $path;

      ? From what I see from the documentation, the -d operator doesn't change $!, does it?
      -- 
      Ronald Fischer <ynnor@mm.st>
Re: Directory creation in Perl using mkdir
by lancer (Scribe) on Apr 04, 2011 at 08:03 UTC

    This method uses the OS shell for checking the existance of a directory:

    $dirname = 'x'; $exists = ` if [ -d '$dirname' ] ; then echo 1; else echo 0; fi `; $exists =~ s/\n$//; if ($exists eq '1') { print "exists"; } else { print "not exists"; } print "\n";

    Hope this helps...

      Why use a shell? However, if you want to, then it is much easier than that:
      $dirname = 'x'; system("[ -d '$dirname' ]"); # $? >> 8 (or the return value from system >> 8) is 0 on success if ($? >> 8) { print "does not exist"; } else { print "exists"; } print "\n";
      However this does not solve the issue with mkdir, so I can't see how using a shell would buy you anything.

        Thanks, cdarke, I was looking for a way to bring back the result of a shell command with the backtick operator... (and I didn't find it)

        So actually it's possible with the system() call.

        As for using the shell, it wasn't clear for me what was the original poster's problem with the mkdir() call, what was exactly failing.

        It appears to be a race condition from what he described, or it can be something else. By moving out the test of the directory's existance from Perl and into the shell, it's possible to eliminate the possibility of other errors inside Perl. I would have suggested to move out the `mkdir` command to the shell too.

        If the problem would still appear when the commands are executed in the shell, then it's not a Perl issue anymore.

        On the other hand, if moving out the commands to the shell would eliminate the issue, then it was a problem somewhere inside Perl, but it would be solved.

        My bet is that it's a problem with how Perl accesses the filesystem, and the "unless ( -d $dir ) { }" test is failing because it's not picking up the change fast enough (the creation of the dir by the other script instance), so it fails to detect it. Maybe it is some kind of a cache issue. (I have no idea, this is just a guess). But if this is the actual problem, then moving out the commands to the shell would probably solve it... (another guess). If not, then it's probably a real race condition.