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

Hi folks,

I have a script that's two fold (it's actually two scripts). The first part grabs a recursive list of files and folders in a directory structure, and strips out CVS files. It then dumps them to a config file that's just a staight line by line list of the files, which I in turn read back into another script.

I realize this is a pretty newbie question, so I need to precursor this with the fact that I'm very, very new at Perl and most of my scripting skill is based on searches here, google and O'Reilly books. :)

Anyhow, my script outputs files, but puts in the "./" before the filename. So my config file reads as:

./filename.x
./otherfile.x

etc, etc

I'd like to strip out the leading "./" before putting them into the output file, so that I can use the second script to run a "cvs co" on each line in the config file.

Without further ado, here's my script, with the header spam stripped:
use strict; use Data::Dumper; use IO::File; ###################################################################### +##### # Helper sub routine calls ###################################################################### +##### system ("clear"); print "Building project config.\n\n"; sub find { my ( $dir ) = @_; my $dh; my @theseFiles; + opendir $dh, $dir or return; while(my $file = readdir $dh ) { next if $file =~ /^\.{1,2}$/; my $full_file = "$dir/$file"; if ( -d $full_file ) { push(@theseFiles,find($full_file)); } else { push(@theseFiles,$full_file); } } closedir $dh; return @theseFiles; } ###################################################################### +##### # Main routine starts here ###################################################################### +##### ## Grab all files ## my @files = find('.'); ## Filter out CVS ## my @noCvsDirs; foreach my $file (@files) { push(@noCvsDirs,$file) unless(($file =~ /\/CVS\//) || ($file =~ /\ +~/)); } ## Save out to config file hwconfig.cfg ## #my $dump = Data::Dumper->new([\@noCvsDirs])->Purity(1)->Indent(0); #my $state = $dump->Dump(); my $state = join("\n",@noCvsDirs); my $fh = new IO::File(">hwconfig.cfg"); die("Checkpoint failed for hwconfig.cfg - $!\n") unless($fh); my $bytes = $fh->syswrite($state, length($state)); $fh->close(); unless($bytes == length($state)) { die("Checkpoint failed, incomplete write for hwconfig.cfg\n"); } else { print "Saved $bytes bytes into file: hwconfig.cfg\n"; } exit;


If someone could tell me the best way to remove the "./" from each filename that gets pushed to the array, I'd really appreciate it.

Thanks much!

Replies are listed 'Best First'.
Re: Need help stripping characters in an array please.
by ikegami (Patriarch) on Dec 08, 2004 at 05:02 UTC

    way 1: (Doing it late)

    foreach my $file (@files) { next if $file =~ m#/CVS/#; next if $file =~ m#~#; $file =~ s#^\./##; push(@noCvsDirs, $file); }

    way 2: (Doing it early)

    my $full_file = ($dir eq '.' ? $file : "$dir/$file");

    way 3: (Doing it early, the portable way!!)

    use File::Spec (); my $full_file = ( File::Spec->canonpath( File::Spec->catfile($dir, $file) ) );

    * The \ in /\~/ can be removed: /~/

    * m#/CVS/# is more readable than /\/CVS\//.

    * m#/CVS/# and m#~# can actually be combined into m#/CVS/|~#.

Re: Need help stripping characters in an array please.
by sgifford (Prior) on Dec 08, 2004 at 05:17 UTC

    I would do this using map; something like:

    my $state = join("\n",map { s{^\./}{}; $_} @noCvsDirs);

    In fact, you can replace the entire main routine of your program with:

    my $fh = new IO::File(">hwconfig.cfg") or die "Couldn't open hwconfig.cfg: $!\n"; print $fh join("\n", map { s{^\./}{}; $_ } grep { !/CVS/ and !/~$/ } find('.')) or die "Couldn't write to hwconfig.cfg: $!\n"; close $fh or die "Couldn't close hwconfig.cfg: $!\n";
    The find('.') returns the list of filenames, the grep removes any that have CVS in them, and the map removes the leading dot-slash.

    If you still want to write everything to a string first, it should be straightforward to modify.

    Also, File::Find is designed to do exactly what you're doing, though in this case I'm not sure it would actually be easier to learn how to use it than to just write it how you have it.

Re: Need help stripping characters in an array please.
by larryp (Deacon) on Dec 08, 2004 at 05:03 UTC

    Try this:

    #!/usr/bin/perl -w use strict; while( <DATA> ) { $_ =~ s{^\./}{}; # This regular expression does the work. print $_; } __DATA__ ./file1.txt file1./file2.log ../file3.txt file4.cpp

    The work is being done by the line with the comment. The regular expression checks the beginning of each item for the './' and replaces it with nothing if it exists. (The ^ anchors it to the beginning of the line, which is why it doesn't make the replacement where the './' doesn't fall at the beginning of the line.)

    Incorporate that regular expression into your loop and you should be good to go.

    HTH,

    /Larry

    Update: Oops! Forgot to backslash the dot in the regex. :)

Re: Need help stripping characters in an array please.
by trammell (Priest) on Dec 08, 2004 at 04:59 UTC
    How about:
    for (@files) { s(^\./)(); }