The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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.