This is an advance chapter of Learning Perl 6 by Randal L. Schwartz and brian d foy. It is incomplete and may contain merely notes or an outline for future work. It may also contain sections of their writing from other sources, although these are noted where possible.

This work is copyrighted under a contract between O'Reilly Media and the authors, and you cannot repost it or distribute it without permission.

Prerequisites

list the Perl 6 items on which this chapter depends. This will help determine the order of chapters and the topics to discuss.

Working with Directories

Getting a list of files

At most command lines, whether that's unix, DOS, or something else, we can write a pattern to stand in for a list of files whose name match that pattern. This is called globbing[1]. We can use this with the unix ls command (dir on DOS> to list all of the files with the .p6 extension:


$ ls *.p6
XXX: file list

It's the shell that does that expansion, not the ls command, so we can do the same thing for our Perl 6 program:


$ ./show_files.p6 *.p6
XXX

In our program, we get the command line arguments in the global array @*ARGS, so we can go through that list and print each filename that matched:


XXX: program to print the filenames.

We don't need the command line though, because we can do this inside our program using either a glob or a directory handle.

XXX: note about hidden files.

XXX: note about . and ..

Globs

The glob operator does the same thing that the shell does, but from within our program. It uses the same pattern as the shell.


my @files = glob( '*' );

If we want to get the hidden files too, we


my @all_files = glob( '.* *' );

To get just the hidden files, we


my @hidden = glob( '.*' );

So far all of those get the files in the current working directory. The glob pattern can handle sub-directories too (as long as we have the right syntax for my particular operating system):


my @jane_files = glob( "jane/*" );

When we look at those files, we see that they have their sub-directory name attached to them:

We can even merge the list of files from more than one sub-directory:


my @merged_files = glob( "george/* jane/*" );

In the object-oriented syntax,


$string.glob

Directory handles

We can open a directory handle to get filenames one at a time. The glob syntax gave us back a (potentially big) list of filenames all at once, but sometimes we only want to get the next filename. We'll do our processing and get another filename when we're done. That way, we don't waste a lot of memory storing the entire list of filenames. This is similar to reading standard input one line at a time instead of all at once.

To get a directory handle, I use the opendir method on the scalar that holds the directory name.


my $directory = 'Rosie';

my $dirhandle = $directory.opendir err die "Could not open $directory: $!";

for =$dirhandle -> $file {
    say "I found $file in $directory";
    }
    
$dirhandle.close;

Directory trees

Recursive


#!/usr/local/bin/pugs

my $start_dir = @*ARGS[0] // '/Users/brian/Desktop';


list_dir( $start_dir, 0 );


sub list_dir ( Str $directory, Int $level ) {

#XXX 	my $dh = $directory.opendir err return;

    my $dh = try{ $directory.opendir } || return;
    
    for $dh.readdir -> $file {
        next if $file ~~ /^\.\.?$/;
        say( "\t" x $level	~  $file );

        next unless "$directory/$file" ~~ :d;
        list_dir( "$directory/$file", $level + 1 );
        }
    }

Iterative

With a reference:


#!/usr/local/bin/pugs

my @dirs = [ @*ARGS[0] // '/Users/brian/llama6/Scripts', 0 ];

while( my $pair = @dirs.shift )
    {
    my( $directory, $level ) = @($pair);
    say "Directory $directory, level $level";
    
    my $dh = try{ $directory.opendir } || next;
        
    for $dh.readdir -> $file {
        next if $file ~~ /^\.\.?$/; next if $file ~~ '.svn';
        
        say( "\t" x $level	~  $file );

        @dirs.unshift( [ "$directory/$file", $level + 1 ] ) if "$directory/$file" ~~ :d;
        
        }	
    }
    

Without the reference:


#!/usr/local/bin/pugs

my @dirs = @*ARGS[0] // '/Users/brian/llama6/Scripts', 0;

while( my $directory = @dirs.shift )
    {
    my $level = @dirs.shift;
    say "Directory $directory, level $level";
    
    my $dh = try{ $directory.opendir } || next;
        
    for $dh.readdir -> $file {
        next if $file ~~ /^\.\.?$/; next if $file ~~ '.svn';
        
        say( "\t" x $level	~  $file );

        @dirs.unshift( "$directory/$file", $level + 1 ) if "$directory/$file" ~~ :d;
        
        }	
    }

Creating and removing directories

Changing directories

* chdir

Differences to Perl 5

* no more <> syntax

Further reading

List resources for the topic, especially since things will change

Exercises

See Section XX in Appendix A for the answers to these exercises:

  1. 1.
  2. 2.