in reply to make_path for creating directory tree

Can I do the same again? Can I say that "I want 30 folders, from AB0001 up to AB0030; also 50 folders, from BC0001 up to BC0050" and so on?
Something like this:
use strict; use warnings; my @dirs = ( make_seq( 'AB', 6, 1, 30 ), make_seq( 'BC', 6, 1, 50 ), ); for my $dir (@dirs) { for my $subdir ( make_seq( 'S', 6, 1, 100 ) ) { print "$ENV{HOME}/$dir/$subdir\n"; } } sub make_seq { my ( $prefix, $total_length, $from, $to ) = @_; my $length = $total_length - length($prefix); my $format = "${prefix}%0${length}d"; return map sprintf( $format, $_ ), $from .. $to; }
If that looks like what you want, replace print with make_path (and remove \n, obviously). Note that make_path doesn't understand ~ (tilde), but you can use $ENV{HOME} instead of tilde.

Replies are listed 'Best First'.
Re^2: make_path for creating directory tree
by fasoli (Beadle) on Dec 23, 2015 at 17:13 UTC

    Omg this is like magic :D Or like a Christmas present. It does what I want, yes. Although I don't understand some bits, so if you could be so kind and explain them you'd be helping me fill one more page in my Perl notebook! I need to understand how stuff works otherwise I'll soon be asking the same things again and annoy you all.

    So:

    1. my $length = $total_length - length($prefix);

    my $format = "${prefix}%0${length}d";

    ^

    What is happening here? So in the first line, you subtract the length of the prefix from the total length, which is 6. Ok, I got that. But then in the second line, I don't get it. Why "${prefix}" and not "{$prefix}"? Oh I think I just got the next bit: "%0${length}d" -> that says we need "length"-many zeros, whatever the value of $length is from above.

    2. return map sprintf( $format, $_ ), $from .. $to;

    ^

    I'm really lost with that one. If the for loop that goes through @dirs already does the job, shouldn't return map be before the for loop? Also why is it return map and not map?

    3. Actually in general what confused me is that I thought Perl reads the script line by line from top to bottom. Now in this example we first use make_path and then set the subroutine. Shouldn't the subroutine be on top of everything else?

    I know you probably get this all the time but I do apologise for the silly questions - I have already started reading the stuff on subroutines, map, make_path and so on, but a bit of discussion helps greatly.

    Thank you for the help, this page is amazing for solving Perl problems and answering questions.

      I need to understand how stuff works otherwise I'll soon be asking the same things again and annoy you all.

      AnonyMonk has given a pretty good explanation above, but you need to understand where to look stuff up. That way, you'll be able to answer your own questions when the time comes that everyone else is too busy or too annoyed to answer them for you.

      ... I thought Perl reads the script line by line from top to bottom. Now in this example we first use make_path and then set the subroutine. Shouldn't the subroutine be on top of everything else?

      In Perl, a subroutine can, in most cases, be defined anywhere in the program and at any time (compile- or run-time) just as long as it is defined when it is actually called at runtime (see perlintro and perlsub). The only examples I can think of that require prior definition or declaration of a function involve prototypes — but don't involve yourself with prototypes unless you have to; see Far More than Everything You've Ever Wanted to Know about Prototypes in Perl -- by Tom Christiansen.


      Give a man a fish:  <%-{-{-{-<

        The only examples I can think of that require prior definition or declaration of a function involve prototypes
        well, there're also function calls without parens :)

        Yep, of course I didn't want a free Perl lesson and I had already started looking stuff up :) Although some times (like now) you need a bit of explaining about what things are, because for example I spent a good bit last night googling "return map perl" and of course no tutorials came up.

        Thanks for clarifying about the subroutine! :)

        Happy holidays!

      Although I don't understand some bits, so if you could be so kind and explain them you'd be helping me fill one more page in my Perl notebook!
      Sure. Feel free to ask any questions.
      my $length = $total_length - length($prefix); my $format = "${prefix}%0${length}d";
      $format just makes a format string for sprintf. It seems you're not familiar with that function. It is modeled after the famous printf function in the C programming language. I recommend you to just search the internet for something like "printf tutorial". There must be lots of those, and it's a very useful function in general.

      The goal of the line my $format = "${prefix}%0${length}d"; is to produce a string like "AB%04d", which is a format string for sprintf.

      Why "${prefix}" and not "{$prefix}"?
      ${length} is actually a more useful example here. You know that you can interpolate variables in strings like so:"$variable". Suppose that I used $length instead of ${length}: "${prefix}%0$lengthd". Then Perl would've tried to find variable $lengthd (notice the d at the end), and there's no such variable. You can use curly braces to disambiguate: "${length}d". Now it's clear (to Perl) that $ goes with length, while d is just a character and is not a part of the variable's name.

      And why ${prefix} instead of just $prefix? Just for symmetry with ${length}.

      Another way to write it:
      my $format = $prefix . '%0' . $length . 'd';
      Actually in general what confused me is that I thought Perl reads the script line by line from top to bottom. Now in this example we first use make_path and then set the subroutine. Shouldn't the subroutine be on top of everything else?
      Actually, no. Variables work like that, but not subroutines. I always put all subroutines at the bottom. That's different from some other programming languages and UNIX shells - in those, functions must also be put on top before you can actually use them - but not in Perl.
      return map sprintf( $format, $_ ), $from .. $to;
      This is a pretty dense line. map is another famous function, from functional programming languages. You should read it right-to-left (like in Arabic):
      $from .. $to
      That creates an anonymous list of numbers, starting with $from and ending with $to. For example, if $from is 1 and $to is 5, it will create (1,  2, 3, 4, 5). It's anonymous because it doesn't have a name and exists only temporarily in Perl's virtual machine.
      map sprintf( $format, $_ ), ANONYMOUS_LIST
      map acts like a loop. It loops over anonymous list and invokes sprintf for each element of that list. map puts the result into another anonymous list. Like so:
      for (ANONYMOUS_LIST) { STRING = sprint( $format, $_ ); put STRING into ANOTHER_ANONYMOUS_LIST; }
      and then return returns ANOTHER_ANONYMOUS_LIST from the make_seq function.

        Ok I just finished studying this :) I understand everything now and I will read on subs, map and printf. Thank you so much! :)

        edit: Actually I was thinking I'd like to make it a bit more "organised" especially in case I add more subfolders in the future, so I made this slight change (nothing really, just specified the subdirectories in the beginning). It looks like it's correct and the result is what I want - sorry, just getting paranoid that I might have messed the subroutine up, that's the only reason why I'm posting this tiny change.

        my @dirs = ( dir_tree( 'AB', 6, 1, 2 ), dir_tree( 'BC', 6, 1, 2 ), ); my @subdirs = ( dir_tree( 'F', 6, 1, 5 ), ); for my $dir (@dirs) { for my $subdir (@subdirs) { print "/results/$dir/$subdir \n"; } }
Re^2: make_path for creating directory tree
by Anonymous Monk on Jan 22, 2016 at 16:52 UTC
    Hi all! I have been trying to edit the above example in order to create a directory tree that goes: structure/molecule/step. As in, /struct1/AB0001/S00001/ and /struct2/BC0001/S00001/. So, struct1 corresponds only to molecule AB, and struct2 corresponds only to molecule BC. However, both AB and BC files should have the same "steps" subdirectories in them, each time from S00001 to whatever step we are calculating at the time. With that in mind, I figured to first create the "parent" directories struct1 and struct2 and then, in another loop, to put in the relevant subdirectories. This however gives me the error "Use of uninitialized value $structs in concatenation (.) or string".
    #/bin/perl/ use strict; use warnings; use File::Path qw(make_path); my @structs; my $structs; my @molecs; my $molecs; my @steps; my $steps; # Parent structure directory. @structs = ( dir_tree( 'struct', 7, 1, 1), dir_tree( 'struct', 7, 2, 2 ), # dir_tree( 'struct', 7, 3, 3 ), # dir_tree( 'struct', 7, 4, 4 ), # dir_tree( 'struct', 7, 5, 5 ), # dir_tree( 'struct', 7, 6, 6 ), ); # Subfolder molecule directory. @molecs = ( dir_tree( 'AB', 6, 1, 1 ), dir_tree( 'BC', 6, 1, 1 ), # dir_tree( 'XZ', 6, 1, 4 ), # dir_tree( 'XY', 6, 1, 4 ), # dir_tree( 'QU', 6, 1, 4 ), # dir_tree( 'QE', 6, 1, 4 ), ); # Subfolder steps directory. @steps = ( dir_tree( 'S', 6, 1, 2 ), ); for $structs (@structs) { make_path "/media/RAIDstorage/home/results/$structs"; } for $molecs (@molecs) { for $steps (@steps) { make_path "/media/RAIDstorage/home/results/$structs/$molecs/$s +teps"; } } # Define subroutine here. sub dir_tree { my ( $name, $total_length, $from, $to ) = @_; my $length = $total_length - length($name); my $format = "${name}%0${length}d"; return map sprintf( $format, $_ ), $from .. $to; }
    If I use an integrated loop, like so
    for $structs (@structs) { for $molecs (@molecs) { for $steps (@steps) { make_path "/media/RAIDstorage/home/results/$structs/$molecs/$s +teps"; } } }
    I get no errors but the wrong directory tree. That way, all $molecs are added in both $structs, when I want AB to go only in $struct1 and BC to go only in $struct2. I also tried using two different subroutines, like sub1 and sub2, but it also gave me the same error, "uninitialized value". Is the answer the two different subroutines, only I did it wrong? Any hints as to what I'm messing up? Thank you all!
      Oops sorry, it looks like I forgot to log in when posting the above.
        for my $molecs (@molecs) { my $structs = shift @structs; ## Add for my $steps (@steps) { make_path "/media/RAIDstorage/home/results/$structs/$molecs/$steps +"; } }
        poj
Re^2: make_path for creating directory tree
by fasoli (Beadle) on Mar 02, 2016 at 17:51 UTC
    Reviving an old thread, but I wanted to ask if it is possible to have a different increment instead of 1, for $from .. $to in the above script? An increment of 5 or 10 or something? Like when in a for loop one could use for ($i=1; $i=100; $i+5) or something like that?