[comp.lang.perl] How about building in directory-tree-walking? like "ftw

sahayman@iuvax.cs.indiana.edu (Steve Hayman) (03/23/90)

I've been trying to pay attention but I don't know if this has
come up before.  Anyway -

How about perl having some sort of built-in directory tree walking?
(Or a perl library routine for this.) Something like the ftw() routine
that many versions of Unix have, that lets you walk through a directory
tree and call a specified function for each thing in the tree.

I know I could open a pipe from "find ... -print" but I find it to be
a pain trying to deal with the different find options on different
machines.  Some machines have -xdev, some have -mount, some have -prune
and so on.  It would be great if perl had some sort of tree-walking
built in.  Then I could make fancier selections than what 'find' lets me do.

Just a thought.  Perl is great.  Thank you Larry.
Steve

-- 
Steve Hayman    Workstation Manager    Computer Science Department   Indiana U.
sahayman@iuvax.cs.indiana.edu                                    (812) 855-6984

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (03/23/90)

In article <39362@iuvax.cs.indiana.edu> sahayman@iuvax.cs.indiana.edu (Steve Hayman) writes:
: 
: How about perl having some sort of built-in directory tree walking?
: (Or a perl library routine for this.) Something like the ftw() routine
: that many versions of Unix have, that lets you walk through a directory
: tree and call a specified function for each thing in the tree.
: 
: I know I could open a pipe from "find ... -print" but I find it to be
: a pain trying to deal with the different find options on different
: machines.  Some machines have -xdev, some have -mount, some have -prune
: and so on.  It would be great if perl had some sort of tree-walking
: built in.  Then I could make fancier selections than what 'find' lets me do.

On the other hand, you can just mutate the following into what you want.
This actually runs faster than find on my system, but that's because it's
only looking at the names in leaf directories, not the inode.  It also
assumes that directory link counts work in the standard fashion.

Larry

#!/usr/local/bin/perl

&dodir('.');
sub dodir {
    local($dir,$nlink) = @_;
    local($dev,$ino,$mode);
    ($dev,$ino,$mode,$nlink) = stat('.') unless $nlink;	# first time

    opendir(DIR,'.') || die "Can't open $dir";
    local(@filenames) = readdir(DIR);
    closedir(DIR);

    if ($nlink == 2) {			# this dir has no subdirectories
	for (@filenames) {
	    next if $_ eq '.';
	    next if $_ eq '..';
	    print "$dir/$_\n";
	}
    }
    else {				# this dir has subdirectories
	for (@filenames) {
	    next if $_ eq '.';
	    next if $_ eq '..';
	    $name = "$dir/$_";
	    print $name,"\n";

	    ($dev,$ino,$mode,$nlink) = stat($_);
	    next unless ($mode & 0170000) == 0040000;

	    chdir $_ || die "Can't cd to $name";
	    &dodir($name,$nlink);
	    chdir '..';
	}
    }
}

viktor@cucumber.Princeton.EDU (Viktor Dukhovni) (03/23/90)

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) writes:


>On the other hand, you can just mutate the following into what you want.
>This actually runs faster than find on my system, but that's because it's
>only looking at the names in leaf directories, not the inode.  It also
>assumes that directory link counts work in the standard fashion.

	How did you know I was writing a fast tree symbolic copy? :-)
With a modified version of dodir,  it got even faster,  Thanks.

	And by the way here is a program that when duplicating
large trees does a lot of mkdirs!  Though forking once for 100
directories seems not too expensive.

---------------------------------------
#! /usr/bin/perl

( $#ARGV != 1 )       &&
    die "Usage: $0 <src> <dst>\n" ;

($tail=$src=shift) !~ m,^/, &&
    die "Use full src path name\n" ;

($dst=shift) !~ m,^/, &&
    die "Use full dst path name\n" ;

$tail =~ s,.*/,, ;
$dst .= "/$tail" if ( -d $dst  && ! ($dst =~ m,/$tail$,) );

if ( ! -d "$dst" ) {
    system "mkdir",$dst ; die "\n" if $? ;
}

chdir($src) || die "Could not chdir to $src\n" ;
$np = $nd = 0 ;
&dodir("",0);

chdir($dst) || die "Could not chdir to $dst\n" ;

while( $#dirs >= $[ ) {
    system("mkdir",splice(@dirs,-100,100)) ;
    die "\n" if $?;
}

for(@pathnames) {
    symlink($src."/".$_,$_) || die "$_: symlink failed\n";
}

;;
;;
;;
sub dodir {
    local($dir,$nlink) = @_;
    local($dev,$ino,$mode);
    ($dev,$ino,$mode,$nlink) = stat('.') unless $nlink;	# first time

    opendir(DIR,'.') || die "Can't open $dir";
    local(@filenames) = readdir(DIR);
    closedir(DIR);

    if ($nlink == 2) {			# this dir has no subdirectories
	for(@filenames) {
	    $pathnames[$np++] = "$dir$_" if ( $_ ne '.' && $_ ne '..' );
	}
    }
    else {				# this dir has subdirectories
	for (@filenames) {
	    next if $_ eq '.';
	    next if $_ eq '..';
	    $name = "$dir$_";

	    ($dev,$ino,$mode,$nlink) = stat($_);
	    if (($mode & 0170000) != 0040000) {
	        $pathnames[$np++] = $name ;
		next;
	    }
	    $dirs[$nd++] = $name ;

	    chdir $_ || die "Can't cd to $name";
	    &dodir($name."/",$nlink);
	    chdir '..';
	}
    }
}
	Viktor Dukhovni <viktor@math.princeton.edu>	: ARPA
		<...!uunet!princeton!math!viktor>	: UUCP
	Fine Hall, Washington Rd., Princeton, NJ 08544  : US-Post
		+1-(609)-258-5792		 	: VOICE