Rand Stats

The uploading author of cpan:LEMBARK does not match the META author of CPAN:lembark.




FileSystem::Parent - Return Seq of elements above a given point in the filesystem.


use FileSystem::Parent;

# list of dirs at or above the executable's directory.

my $found   = scan-up;

# the Seq is evaluated lazily, or can be converted to
# a list on return.

say '#Found: ', $found.join( "\n#\t" );

my @dirz    = $found.eager;

# return the list of dirs above a given directory or
# a file:

my $found   = scan-up from => '~/sandbox';
my $found   = scan-up :from( $*PROGRAM-NAME  );

# or use a file as the starting point:

my $found   = scan-up :from( 'SYS$USER:[sandbox]foo.c;3' );
my $found   = scan-up :from( 'c:\\run\\foo.bat' );

# resolve any symbolic links in the input path once
# before tracing up the directory tree.

my $found   = scan-up :resolve;
my $found   = scan-up :resolve, :from( $some_file );

# append 'lib' or a multi-part relatvie path to each
# of the directories reported using the proper syntax
# for the filesystem.

my $found   = scan-up append => 'lib';
my $found   = scan-up append => 'etc/config.yaml';

# or append a file basename, or a subdir.

my $found   = scan-up append => 'etc/config.yaml';

# usually not all tha useful to scan root dir's for 
# Perly modules. skip-root avoids procesing the root
# (e.g., '/') directory. in this case, the list will
# not include any of "/lib" or "c:\lib", or

my $found   = scan-up append => 'lib', :skip-root;

# default filter is 'all'.
# also available: dir, file, exists.

my $found   = scan-up append => 'lib', filter => 'dir';
my $found   = scan-up append => 'etc', filter => 'dir';

my $found   = scan-up append => 'config.yaml', filter => 'file';
my $found   = scan-up append => '.gitignore',  filter => 'exist';

# roll your own filter. basically, grep on the file tree
# above your executable for whatever you like. 
# filter value anything that can smart match with an IO:
# string, regex, block...

my $from    = '~/sandbox/project/cleanup';
my $append  = 'etc/cleanup.confg.yaml';
my $filter  = -> IO $path { $path ~~ :s && $path ~~ :r };

my $found   = scan-up :$from, :$append, :$filter;

# do it verbosely: Reporting all wanted or skipped dirs
# and getting report from FindBin for a default start dir.

my $verbose = True;
my $found   = scan-up :$verbose;


FileSystem::Parent scans up the filetree from a given file or directory searching and filtering for specific items. Unlike scandir(3) or File::scan-up it returns a short list -- at most the depth of the file tree at the starting point.

It's use is in locating direcories or files for library inclusion or configuration based on an executable's location in the filesystem.

The default behavior simply returns the absolute, possibly resolved, path of each directory above the starting point. Supplying a relative path via "append" allows searching for sub-dirs or configuration files relative to the executable; adding a filter locates usable items along the path.

One example is the use via FindBin::libs to append => 'lib', filter => 'dir' and effectively '-I' the existing dir's below /lib at compile time based on the executable's location in the filesystem. Another is using append => "etc/$name.config.yaml", filter => 'file' and locating usable configuration files at increasing distances.

The canned filter are listed below; a user-supplied filter is compared by smart-matching each path element as an IO to the argument, allowing regexen or blocks to perform any actions they require (including initializing the paths if necessary via a block).


All parameters are named (i.e., no positionals). All have defaults.

are all valid in appropriate places. So long as Perl6 IO can successfully check that it exists, is a directory, and take the parent of a non-dir it'll work.

This is affected by the "resolve": starting from a symlink executable may leave the file tree completely outside of where the running executable lives.

Default is FindBin::Bin (which may simply be the current working directory for in interactive invocation).

Depding on your value of append, different filters may be meaningless (e.g., 'dir' with a config file basename).

Defaults is '' (i.e., append nothing).

Non-string values can include regexen, blocks... anything for which

$path.IO ~~ $filter

is valid.

A block should take an IO as argument and return a Boolean. For example, to return ./etc dirs which contain "config.yaml" files for the current executable:

my $filter  = -> IO $path 
    $path.add( 'config.yaml' ) ~~ :e

my $dirz    = scan-up append => 'etc', :$filter;

this will leave $dirz as a sequence of 'etc' dir's which do contain the requested basename -- not a lise of valid config files.

Default is Stringy 'all', which takes everything.

will not return "/lib" on *NIX.

This saves the caller from having to convert the lazy list to an array in order to 'pop' the last item or find some way to avoid processing the last item returned from the seq.

Default: False (i.e., include the root in processing).

Defaults to False.

Defaults to False.



Describes behavior of Bin used for default start along with its use of verbose & resolve.


Steven Lembark lembark@wrkhors.com


Copyright 2019 Steven Lembark

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0 or any later version.