Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?

How do I recursively create a directory tree/filesystem?

by fensterblick (Initiate)
on Jul 21, 2002 at 23:38 UTC ( #183899=categorized question: print w/replies, xml ) Need Help??
Contributed by fensterblick on Jul 21, 2002 at 23:38 UTC
Q&A  > directories


I would like to create a directory tree up to a certain "depth". This directory tree has sub directories, and those sub directories have even more subdirectories, all up to a certain depth.

I've spent half of the day trying to do this recursively, but for some reason, only the first directory of each level has subdirectories.

FYI, here is a stripped down version of the code I have:
#!/usr/bin/perl -w use Cwd; sub MakeDirs($$$) { #my $current_dir = getcwd(); my($base, $num_dirs, $depth) = @_; #if depth = 0, no more subdirectories need to be created if($depth == 0) { return 0; } #change the directory chdir $base; #Create directories on the current level my $dir_name = "a"; for($x = 0; $x < $num_dirs; $x++) { mkdir $dir_name; $dir_name++; } #recurse over subdirectories $dir_name = "a"; for($x = 0; $x < $num_dirs; $x++) { MakeDirs($dir_name, $num_dirs, $depth-1); $dir_name++; } return 0; } MakeDirs("/hpq/", 2, 3);
I would use File::Recurse, but the files/directories haven't been created yet. Thanks for any suggestions. This script is driving me crazy!

Answer: How do I recursively create a directory tree/filesystem?
contributed by rob_au

For a more simple solution, you may want to look at the mkpath function exported from the File::Path module that is part of the core installation. Alternatively, if you are looking at writing your own function for educational purposes, you might want to look at this thread where I contributed (based on an earlier post from mirod) a more portable solution using File::Spec using a loop over the path components rather than employing recursion (Recursion makes it very easy to write bad code - Whenever I see recursive code my first urge is to refactor immediately :-).

The code ...

sub create_dirs { use File::Spec; my $file = shift; my $dir; foreach ( File::Spec->splitdir( (File::Spec->splitpath( $file ))[1 +] ) ) { $dir = File::Spec->catdir($dir, $_); next if -d $dir; mkdir( $dir ) || die $!; } }

Answer: How do I recursively create a directory tree/filesystem?
contributed by Ionitor

The major problem here is that you're using chdir and not returning to the right directory afterwards. Since you're changing the directory of the program, the recursion doesn't work. The fastest way to fix the problem is to add chdir '..'; before return 0;. There's a couple of other changes you might want to make described below, one of which is to avoid chdir entirely.

First, I added use strict and made a couple changes to make that work. I also removed the pointless return 0;.

Next, I removed the chdir and changed the recursion line to:

MakeDirs("$base/$dir_name", $num_dirs, $depth - 1);
This avoids changing the directory and leaves you where you were if the program quits midway through for some reason.

Finally, I removed the mkdir loop, and added mkdir $base; near the top of the program. This makes every recursion make one directory, which is how I like my recursion to work. It also has the added advantage of creating the initial directory if it doesn't exist.

My finished code follows:

#!/usr/bin/perl use strict; use warnings; sub MakeDirs($$$); MakeDirs("/hpq", 2, 3); sub MakeDirs($$$) { my($base, $num_dirs, $depth) = @_; mkdir $base; #if depth = 0, no more subdirectories need to be created if($depth == 0) { return 0; } #Recurse through the directories my $dir_name = "a"; for(my $x = 0; $x < $num_dirs; $x++) { MakeDirs("$base/$dir_name", $num_dirs, $depth - 1); $dir_name++; } }
Answer: How do I recursively create a directory tree/filesystem?
contributed by lemming

For the recursive mkpath solution: Of course reading the docs helps.

use strict; use warnings; use File::Path; my $name = shift; my $branch = shift; my $depth = shift; # We really should be checking arguments # Bad things happen if $branch or $depth is 0 print "Creating directory ",$name," with ", $branch, " branches to the depth of ", $depth, "\n"; my $aref = MkTree($name, $branch, $depth, [] ); foreach my $path ( @$aref ) { print $path, "\n"; } mkpath($aref, 1, 0777); exit; sub MkTree { my ($name, $branch, $depth, $aref) = @_; my $start = "a"; foreach my $pos (1..$branch) { my $let = chr(ord($start) + $pos - 1); if ( $depth == 1 ) { push(@$aref, join('/', $name, $let) ); } else { MkTree( join('/', $name, $let), $branch, $depth-1, $aref); } } return $aref; }
Answer: How do I recursively create a directory tree/filesystem?
contributed by Anonymous Monk

I'm fairly new to all this so if anyone has pointers for my script please speakup.

My solution was to use the glob() function and loop through the results looking for the next set if directories. I guess you could count the number of times the loop is run to limit the depth or you could have a target directory to stop and break out of the loop.

The purpose of this script is to make links to all the files it finds. It is a helpful script to convert files from one system to another where users have comeup with crazy files names. Hope this helps.

#!/usr/bin/perl -w #Do Not run as Root, this may cause dirs to be link and corrupt the; #file system; my $start; my @in; my @big; my @next; my $level; my $dir; my @files; my $file; my $z=0; my $target; print "\n\n ***********************************\n"; print "This script will create (symbolic) links recursively\n"; print "to all files in the specifiled directory and also create\n"; print "the links from the directory specified\n"; print " ***********************************\n\n"; print " Please enter the starting target directory\n"; print " (don't end with a slash)\n"; $start=<STDIN>; chomp $start; print " \nEnter the working dir where you would like\n"; print " the links to be placed?\n"; print " (don't end with a slash)\n"; $target=<STDIN>; chomp $target; @in = (glob("$start*/")); @big = (@in); do { @next = (@in); @in = (); foreach $level (@next) { @in = (@in, (glob("$level*/")))} @big = (@big, @in); @next = ();} while $in[0]; foreach $dir (@big) { @files = (@files, (glob("$dir*"))); } foreach $file (@files) { $z=$z+1; $link = "$target"."/"."$z"; print "$z \-\- $file\n"; # to creat a symbolic link change link to symlink; link("$file","$link") || warn "can't link to file $file\n"; }

Please (register and) log in if you wish to add an answer

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    and all is quiet...

    How do I use this? | Other CB clients
    Other Users?
    Others contemplating the Monastery: (3)
    As of 2018-07-19 23:40 GMT
    Find Nodes?
      Voting Booth?
      It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?

      Results (421 votes). Check out past polls.