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

Is there a way to test if a directory exists, and if not, create it?

For instance, I am trying to create backups of files in the midst of a program, in a directory named './backup_files', but at runtime, if the directory doesn't exist, I would want to create it. I assume that if it doesn't exist, I'd create the directory via mkdir, but I also assume it's not a good idea to do something like:

opendir || mkdir || die;

Once I knew I had a directory where I wanted it, I'd use File::Copy to create a copy in the backup directory, and then modify the original however I want. It's just that first step that I am unsure of.

Thanks,
Matt

Replies are listed 'Best First'.
Re: Is there a -e test for directories, like there is for files?
by ikegami (Patriarch) on Feb 08, 2007 at 16:28 UTC

    -e does return true for directories (and any other kinds of file).
    -f checks only for plain files.
    -d checks only for directories.

    But do you really need to check if the dir exists? You could just try to make the directory unconditionally.

    # Try to make the directory in case it doesn't exists. mkdir($dir); # or mkpath($dir); opendir($dir) or die("Unable to open dir \"$dir\": $!\n"); ...

    You might be interested in (core module) File::Path's mkpath instead of mkdir. It creates intermediary directories as needed.

      OK, so thanks for the advice, all 3 of you. I tried out your suggestions, and got a temp script up and running. I threw in a bunch of print statements, to see what was happening, and it all looks good. But, something isn't working right. Somehow, the grep statement in the if isn't coming out right. I thought to use
      if (-e $back)
      instead of
      if (grep {$back} @files),
      but I am unsure how to test for files in a different directory..

      #! /usr/bin/perl use strict; use warnings; use File::Path; use File::Copy; use Cwd; print "Your Input: "; chomp(my $filename = <STDIN>); my $dir = getcwd; my $backupdir = 'backup_files'; my $back = $filename . ".bak"; my $backful = $dir . "/" . $backupdir . "/" . $back; print "\$dir is: $dir\n"; print "\$filename is: $filename\n"; print "\$backupdir is: $backupdir\n"; print "\$back is: $back\n"; print "\$backful is: $backful\n"; unless (-e $backupdir) { mkpath($backupdir); } if (-e $backupdir) { if (-d $backupdir) { opendir (DIR, $backupdir) or die $!; my @files = readdir DIR; print "\@files is: \n"; foreach (@files) { print $_, "\n"; } if (grep {$back} @files) { print "Backup copy of $filename exists!\n"; } else { copy ($filename, $backful); print "Backup copy of the file has been created\n"; } } else { print "\$backupdir exists, but it is not a directory\n"; } }

      Any thoughts on where I am going wrong?

      Thanks

        -e $backful

        Update: Specifically, replace

        opendir (DIR, $backupdir) or die $!; my @files = readdir DIR; print "\@files is: \n"; foreach (@files) { print $_, "\n"; } if (grep {$back} @files) { print "Backup copy of $filename exists!\n"; } else { copy($filename, $backful); print "Backup copy of the file has been created\n"; }
        with
        if (-e $backful) { print "Backup copy of $filename exists!\n"; } else { copy($filename, $backful); print "Backup copy of the file has been created\n"; }

        You should check whether the backup is the same, not whether it exists.
        You should check whether the copy succeeded.
        You should fix up your indenting.

        chomp(my $filename = <STDIN>); my $dir = getcwd; my $backupdir = 'backup_files'; my $back = $filename . ".bak"; my $backful = $dir . "/" . $backupdir . "/" . $back;

        May I suggest slightly cleaner alternatives?

        use File::Basename qw( basename ); use File::Spec::Functions qw( catfile ); chomp(my $filenameful = <STDIN>); my $filename = basename($filenameful); my $backupdir = 'backup_files'; my $back = $filename . ".bak"; my $backful = catfile($backupdir, $back);
        use File::Basename qw( basename ); use File::Spec::Functions qw( catfile rel2abs ); chomp(my $filenameful = <STDIN>); $filenameful = rel2abs($filenameful); my $filename = basename($filenameful); my $backupdir = 'backup_files'; my $back = $filename . ".bak"; my $backful = rel2abs(catfile($backupdir, $back));

        They both allow a path to be attached to the input.
        They both use the right directory seperator for the platform being used.
        They both cannonize the path, so foo//bar would become foo/bar.
        The first creates a relative path while the second creates an absolute one.

Re: Is there a -e test for directories, like there is for files?
by liverpole (Monsignor) on Feb 08, 2007 at 16:28 UTC
    Hi mdunnbass,

    Check out mkdir; specifically the -p option.

    You can see if a directory exists with -d.  This tells you with a single command whether the specified path exists, and is a directory.  Of course, you would also presumably need to use -f to make sure a file with the same name does NOT exist:

    # Assuming $path contains the full pathname of the file/directory if (-d $path) { # It's an existing directory } elsif (-f $path) { # It's an existing file }

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Is there a -e test for directories, like there is for files?
by davorg (Chancellor) on Feb 08, 2007 at 16:31 UTC