The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Evo::Fs

VERSION

version 0.0405

SYNOPSIS

use Evo '-Fs; File::Basename fileparse';
my $fs = Evo::Fs->new(root => '/tmp/testfs');

say "/foo => ", $fs->path2real('/foo');
say "foo => ",  $fs->path2real('/foo');    # the same

my $fh = $fs->open('foo/bar.txt', 'w');    # open and create '/foo' if necessary
$fs->close($fh);

$fs->write('a/foo', 'one');                # /tmp/test/a/foo
$fs->append('/a/foo', 'two');              # /tmp/test/a/foo
say $fs->read('a/foo');                    # onetwo
say $fs->read('/a/foo');                   # the same

# bulk
$fs->write_many('/a/foo' => 'afoo', '/b/foo' => 'bfoo');


# copying
$fs->write('/from/d/f' => 'OK');

# copy file
$fs->remove_tree('/to') if $fs->exists('/to');
$fs->copy_file('/from/d/f' => '/to/d/f');
say $fs->read('/to/d/f');    # OK

# copy dir recursively
$fs->remove_tree('/to') if $fs->exists('/to');
$fs->copy_dir('/from' => '/to');
say $fs->read('/to/d/f');    # OK

$fs->sysopen($fh, '/c', 'w+');
$fs->syswrite($fh, "123456");
$fs->sysseek($fh, 0);
$fs->sysread($fh, \my $buf, 3);
say $buf;                                  # 123

# traversing
$fs->find_files(

  # where to start (/ => /tmp/testfs)
  '/',

  # do something with file
  sub ($path) {
    say $path;
  },

  # skip dirs like .git
  sub ($path) {
    scalar fileparse($path) !~ /^\./;
  }
);

$fs->find_files(
  ['/'],
  sub ($path) {
    say "FOUND: ", $path;
  }
);

# FSROOT
use Evo::Fs 'FSROOT';
say join ', ', FSROOT->ls('/');

DESCRIPTION

An abstraction url-like layer between file system and your application. Every path is relative to root.

You wan't be able to do something like this:

my $fs = Evo::Fs->new(root => '/tmp/fs');
my $path = '../fs2/foo';
$fs->write($path);

This is a security protection. But you can use "cd" instead

my $fs2 = $fs->cd('../fs2');
$fs2->write('foo' => 'OK');

EXPORTS

FSROOT

Return a single instance of Evo::Fs where root is /

ATTRIBUTES

root

my $fs = Evo::Fs->new(root => '/tmp/test-root');

METHODS

cd

Create a new Evo::FS instance. Also this is the only way to traverse up

my $fs       = Evo::Fs->new(root => '/tmp/fs');
my $fs2      = $fs->cd('../fs2');
my $fs_child = $fs->cd('child');

copy_file($self, $from, $to)

Copy file, die if already exists

copy_dir($self, $from, $to)

Copy directory recursively, if directory $toexists, replace it content, create it otherwise. And for the children do the same

This functions kinda try to synchronize one path with another. Unlike cp -a, 2 invocations of this functions will lead to the same result (cp tries to check, if directory $to exists and copies $from to it in this case, this functions won't do this)

my $fs = Evo::Fs->new(root => File::Temp->newdir);
$fs->write('/base/child/file' => 'OK');
$fs->make_tree('/copy/child'); # just to show that directory can exist
$fs->copy_dir('/base', 'copy');
say $fs->read('/copy/child/file'); # OK

In this example, directory /copy/child already exists, so a single file /base/child/file will be silenty copied to /copy/child/file

sysopen ($self, $path, $mode, $perm=...)

my $fh = $fs->open('/foo/bar.txt', 'w');

Open a file and return a filehandle. Create parent directories if necessary. See "sysopen" for list of modes

append, write, read, read_ref

$fs->write('/tmp/my/file', 'foo');
$fs->append('/tmp/my/file', 'bar');
say $fs->read('/tmp/my/file');            # foobar
say $fs->read_ref('/tmp/my/file')->$*;    # foobar

Read, write or append a content to the file. Dirs will be created if they don't exist. Use lock 'ex' for append and write and lock 'sh' for read during each invocation

write_many

Write many files using write

sysseek($self, $position, $whence='start')

Whence can be one of:

sysread ($self, $fh, $ref, $length[, $offset])

Call sysread but accepts scalar reference for convinience

syswrite($self, $fh, $scalar, $length, $offset)

Call syswrite

sysopen ($self, $fh, $path, $mode, $perm=...)

$fs->sysopen(my $fh, '/tmp/foo', 'r');

Mode can be one of:

* w Open file for writing. The file is created (if it does not exist) or truncated (if it exists). * wx Like w but fails if path exists. * w+ Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists). * wx+ Like w+ but fails if path exists.

* a Open file for appending. The file is created if it does not exist. * ax Like a but fails if path exists. * a+ Open file for reading and appending. The file is created if it does not exist. * ax+ Like a+ but fails if path exists.

rename($self, $oldpath, $newpath)

Rename a file.

stat($self, $path)

Return a Evo::Fs::Stat object

path2real($virtual)

Convert a virtual path to the real one.

find_files($self, $dirs, $fn, $pick=undef)

$fs->find_files('./tmp', sub ($fh) {...}, sub ($dir) {...});
$fs->find_files(['/tmp'], sub ($fh) {...});

Find files in given directories. You can skip some directories by providing $pick->($dir) function. This will work ok on circular links, hard links and so on. Every path will be passed to $fn->($fh)only once even if it has many links.

So, in situations, when a file have several hard and symbolic links, only one of them will be passed to $fn, and potentially each time it can be different path for each find_files invocation.

See "traverse" for examining all nodes. This method just decorate it's arguments

SKIP_HIDDEN

You can also traverse all files, but ignore hidden directories, like ".git" this way:

use Evo '-Fs FS SKIP_HIDDEN';
FS->find_files('./', sub($path) { say $path; }, SKIP_HIDDEN)

traverse($self, $dirs, $fn, $pick=undef)

Traverse directories and invoke $fn->$path for each child node.

Each file is processed only once no matter how many links it has. So instead of a real filename you may be getting a link and never a real name depending on which one (file or link) was met first

You can provide $pick->($dir) to skip directories, for example, to skip hidden ones. By default all directories are processed

$fs->traverse('/tmp', sub ($path) {...}, sub ($dir) {...});
$fs->traverse(['/tmp'], sub ($path) {...},);

Also this method doesn't try to access directories without X and R permissions or pass them to $pick (but such directories will be passed to fn because are regular nodes)

In most cases you may want to use "find_files" instead.

AUTHOR

alexbyk.com

COPYRIGHT AND LICENSE

This software is copyright (c) 2016 by alexbyk.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.