Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

How to perform recursion in any OS?

by rollec (Initiate)
on Dec 08, 2016 at 00:09 UTC ( [id://1177457]=perlquestion: print w/replies, xml ) Need Help??

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

I'm making a program that allows a user to generate a HTML page of directories in a system. My issue is that I only made the code to work for linux. I need it to work for ALL operating systems. How can I do this?

#!/usr/bin/perl use strict; use warnings; ### This program looks at the current directory and then makes a path +to all the sub directories. ### Step 1: Create a subroutine with Variables for the program. This i +ncludes the following: ### Files, Path and Directories. ### Step 2: make the program list the files and folders of current dir +ectory. Push folders into an array ### Step 3: Print Dirs ### Step 4: Recurse if (@ARGV != 1) { print "Usage: perl $0 [PATH/][TO/]DIR\n"; exit; } my $path = $ARGV[0] ; sub list { my $path=$_[0]; #print "DEBUG - listing [$path]\n"; chomp(my @files = `ls -a $path`); my @dirs; foreach(@files) { if (-d "$path/$_" && $_ ne "." && $_ ne ".."){ push (@dirs , "$path/$_"); } } print "-------------------------------------\n"; print "Generating HTML doc for path = [$path]\n"; open(FHOUT, ">", "$path/index.html") or die "Can't open '$path/index.html':$!\n"; print FHOUT "<!DOCTYPE html>"; print FHOUT "<html><head>" . "<title>listing:</title>" . "</head><body>"; print FHOUT "<ol>\n"; foreach( @files ) { if ($_ ne "index.html" && $_ ne "." && $_ ne "..") { print FHOUT "<li>"; print FHOUT "<a href='$_'>Parent</a>"; } if (-d $_) { print FHOUT "&nbsp;&nbsp;(DIR)"; } print FHOUT "</li>"; #print FHOUT "<br/>\n"; } } print FHOUT "</ol>\n"; print FHOUT "</body></html>"; close(FHOUT); #foreach (@dirs) { # print "$_\n"; #} ####+++++++++++++++ ## Now visit other dirs recursively: foreach (my @dirs) { &list($_); } exit;

Replies are listed 'Best First'.
Re: How to perform recursion in any OS?
by choroba (Cardinal) on Dec 08, 2016 at 00:22 UTC
    Instead of `ls -a $path`, which doesn't work for filenames containing spaces, anyway, use glob, or File::Find or File::Find::Rule, Path::Tiny etc.

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

      I prefer Path::Class for listing directories:

      use Path::Class qw(dir); my @files = map {$_->basename} dir($path)->children;

      Also check out the docs (perldoc Path::Class::Dir) about the "recurse" function.

      In any case, there are many other reasons why you should never write code like `ls -a $path`. Have you thought about the possibility that $path includes the string "; rm -rf /"?

Re: How to perform recursion in any OS?
by shmem (Chancellor) on Dec 08, 2016 at 11:56 UTC

    Never use backticks or qx() without taint mode in a program driven from the web. See perlsec.

    To make your program portable, use the builtins opendir and readdir to read directories. Use File::Spec to assemble paths from directory and file. Here's a quick diff with the changes.

    --- 1177457.pl 2016-12-08 12:36:42.215747438 +0100 +++ 1177457.pl.new 2016-12-08 12:45:35.603768833 +0100 @@ -2,7 +2,7 @@ use strict; use warnings; - +use File::Spec; ### This program looks at the current directory and then makes a path + to all the sub directories. @@ -22,17 +22,20 @@ sub list { my $path=$_[0]; #print "DEBUG - listing [$path]\n"; - chomp(my @files = `ls -a $path`); + my @files = do { + opendir my $dh, $path or die "opendir '$path': $!\n"; + grep !/^\.\.?$/, readdir $dh; + }; my @dirs; foreach(@files) { - if (-d "$path/$_" && $_ ne "." && $_ ne ".."){ - push (@dirs , "$path/$_"); - } + my $f = File::Spec->catpath($path,$_); + push @dirs, $f if -d $f; } print "-------------------------------------\n"; print "Generating HTML doc for path = [$path]\n"; - open(FHOUT, ">", "$path/index.html") - or die "Can't open '$path/index.html':$!\n"; + my $index = File::Spec->catpath($path,'index.html'); + open(FHOUT, ">", $index) + or die "Can't open '$index':$!\n"; print FHOUT "<!DOCTYPE html>"; print FHOUT "<html><head>" . "<title>listing:</title>"

    I didn't tidy the program, you have to do that yourself. There's no call to list() except in that loop whith an empty my() array -

    foreach (my @dirs) { &list($_); }

    - so it doesn't produce any output but warnings (print() on unopened filehandle FHOUT).
    Omit the ampersand from the call to list(). Look it up in perlsub to see what it does.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: How to perform recursion in any OS?
by BrowserUk (Patriarch) on Dec 08, 2016 at 00:49 UTC

    Change this line to:

    chomp(my @files = $^O eq 'MSWin32' ? `dir /b $path` : `ls -a $path`);

    /b is closer to -A than -a, but that is easily worked around if necessary.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: How to perform recursion in any OS?
by Marshall (Canon) on Dec 09, 2016 at 03:06 UTC
    Here is some code that may help you...
    File::Find is a module that is most likely included in your Perl distribution without having to install anything.
    Update: File::Find is a core module, so it is for sure in your installation. There are other similar modules. File::Find does the recursion without you having to think much about it.

    Update:See following posts.. evidently some "system" Perls on Unix machines may not have all of the Core modules! Users are well advised to install their own Perl environment. I do most of my work nowadays on Windows (which doesn't come with Perl) so the issues are different. The code below is multi-platform (I tested on Windows), but you do need File::Find.

    Pay attention to the error messages for invalid command line input.

    #!/usr/bin/perl use strict; use warnings; use File::Find; my $path = shift; # what you expect to happen! # @ARGV contains one thing which is # a path to a directory. This shift removes # that path from @ARGV and it goes to $path # Now check if the "normal case" is correct or not... + usage("no directory specified") if !defined $path; usage("too many arguments. Only one dir allowed") if (@ARGV !=0 ); usage("$path does not exist") if (not -e $path); usage("$path is not a directory") if (not -d $path); sub usage { my $msg = shift; print STDERR "$msg\n"; exit (1); # exit(0) is success, all other values # mean a failure as per common practice } find(\&each_file, $path); # each_file() is called for every # file underneath $path. # Note that a directory is a special # kind of a file. -f tests if this name # is a "simple file". sub each_file { # your code goes below... # $File::Find::name is the current file in this recursive # descent underneath the directory $path # if (-f $File::Find::name) #a simple file (NOT ., .., Or any other d +irectory) { print "$File::Find::name\n"; #current file name # I would make an HTML sub to call here for that name.. } }
      File::Find is a core module, so it is for sure in your installation.

      It would be nice if that were true. Unfortunately, some (many?) vendors choose to provide the perl binary separately from the core modules. This means that it is not only possible to have perl installed on a system without the core modules but in some cases (yes, I'm looking at you, RedHat) it is the common scenario. Thankfully those core modules are usually available elsewhere in the distribution and it is usually a simple matter of using the vendor's package manager to install them.

      This point has been made a few times before. See for example, Re: Why not include version.pm

        Never expect your "system" Perl to do anything else but system-related tasks and do not rely upon it for anything else.

        Install your own Perl (perlbrew is ideal for this) and use that for your tasks. That way you are sure to have a modern/recent Perl that contains the modules and tools you need.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
Re: How to perform recursion in any OS?
by choroba (Cardinal) on Dec 09, 2016 at 15:37 UTC
    Crossposted to reddit/r/perl. It's considered polite to inform about crossposting to avoid duplicate work of hackers not attending both sites.
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2024-03-28 16:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found