README

File::Directory::Bubble

A file-removal tool that

  • optionally bubbles deletion up the directory tree until it hits a non-empty directory;

  • allows for dry runs, only showing you what would be deleted.

Installation

With zef:

  • just running zef install File::Directory::Bubble should work once the module has been indexed;

  • or clone this repo and issue zef install <path-to-cloned-repo>;

  • or clone, cd into the repo, and zef install ..

Usage

The module provides the executable bin/bbrm for access to (what I believe would be) the most common functionality.

First, you can run bbrm --help for a quick breakdown of the usage. For the examples, I will assume access to the tree utility in order to visualize the directory structure that bbrm is meant to alter.

Throughout, assume you're in a directory with the following structure:

$ tree .
.
ā”œā”€ā”€ a
ā”‚Ā Ā  ā”œā”€ā”€ b
ā”‚Ā Ā  ā”‚Ā Ā  ā””ā”€ā”€ c
ā”‚Ā Ā  ā”œā”€ā”€ b1
ā”‚Ā Ā  ā”‚Ā Ā  ā””ā”€ā”€ c
ā”‚Ā Ā  ā”‚Ā Ā      ā””ā”€ā”€ d
ā”‚Ā Ā  ā””ā”€ā”€ b2
ā”‚Ā Ā      ā””ā”€ā”€ c
ā”‚Ā Ā          ā””ā”€ā”€ d
ā””ā”€ā”€ foo.txt

9 directories, 1 file

I will run the bbrm command with the --dry option, so it only shows us what it would remove.

$ bbrm a/b* --dry

Would remove:
<fully-expanded path>/a/b/c
<fully-expanded path>/a/b
<fully-expanded path>/a/b1/c/d
<fully-expanded path>/a/b1/c
<fully-expanded path>/a/b1
<fully-expanded path>/a/b2/c/d
<fully-expanded path>/a/b2/c
<fully-expanded path>/a/b2

As expected, that would remove everything under the directories ./a/b*. On the other hand, the --up flag would also remove the ./a directory, because it would become empty upon removing the other ones:

$ bbrm a/b* --dry --up

Would remove:
<fully-expanded path>/a/b2/c/d
<fully-expanded path>/a/b2/c
<fully-expanded path>/a/b2
<fully-expanded path>/a/b1/c/d
<fully-expanded path>/a/b1/c
<fully-expanded path>/a/b1
<fully-expanded path>/a/b/c
<fully-expanded path>/a/b
<fully-expanded path>/a

In fact, the same would happen if you were to first remove everything at lower levels: empty-directory deletion would still bubble up.

$ bbrm a/b*/c --dry --up

Would remove:
<fully-expanded path>/a/b2/c/d
<fully-expanded path>/a/b2/c
<fully-expanded path>/a/b2
<fully-expanded path>/a/b1/c/d
<fully-expanded path>/a/b1/c
<fully-expanded path>/a/b1
<fully-expanded path>/a/b/c
<fully-expanded path>/a/b
<fully-expanded path>/a

Though again, that only happens with the --up flag. Without it you're only deleting down the directory tree.

$ bbrm a/b*/c --dry

Would remove:
<fully-expanded path>/a/b/c
<fully-expanded path>/a/b1/c/d
<fully-expanded path>/a/b1/c
<fully-expanded path>/a/b2/c/d
<fully-expanded path>/a/b2/c

Other modules in the Raku ecosystem

There's of course File::Directory::Tree, but because it deletes files/directories recursively using unlink and rmdir, it's not easy to build a --dry option on top of it:

If you're doing a dry run you're not actually emptying directories, so rmdir doesn't know what it would remove if you were..

Module functions

The library lib/File/Directory/Bubble.rakumod exports a number of functions, some of which are used by the bbrm utility discussed above.

A summary follows.

sub listParents

sub listParents(
    IO::Path $file
) returns Mu

List the argument's parents, as far up as possible.

sub bbUpWith

sub bbUpWith(
    IO::Path $file,
    &cond
) returns Mu

Starting with a file, walk up its parent list until a callback function (of your choosing) returns false. Returns the list of parents for which the callback holds.

Starting with the $file you pass in, it builds the increasingly longer lists of ancestors, as in

  • $file, parent

  • $file, parent, parent-of-parent

  • etc.

The predicate &cond is called on these lists, so there's quite a bit of generality built into the kinds of conditions you can check for.

The iteration stops when &cond first returns false, giving you back the last list of parents before that happened.

sub noChildrenExcept

sub noChildrenExcept(
    IO::Path $dir where { ... },
    $fList
) returns Mu

Check whether a directory has no children except those in a given list.

sub has1childExcept

sub has1childExcept(
    $dirList,
    $fList
) returns Mu

A check whether, in a lost of directories, the last one's children consist at most of the next-to-last one plus a list you pass as a second argument.

This is a utility function, for use with &bbUpWith above to produce &bbUpEmpty below.

sub bbUpEmpty

sub bbUpEmpty(
    IO::Path $file,
    $fList
) returns Mu

Given a file and a list of other files, bubble up the parent list of the former until you hit directories that have other children, apart from the list you passed and the children you've already walked over.

This function allows the bbrm script to list what it would remove upon passing the --up flag, even during a --dry run.

You can presumably build your own more complicated examples using the more general callback-driven &bbUpWith above.

sub bbDown

sub bbDown(
    IO::Path $file
) returns Mu

Recurse down a directory, retrieving the files/directories under it.

sub smartRm

sub smartRm(
    IO::Path $file
) returns Mu

Unlink a file or remove an empty directory.

File::Directory::Bubble v0.1

remove files recursively, up or down the directory tree

Authors

  • stuart-little

License

Artistic-2.0

Dependencies

Test Dependencies

Provides

  • File::Directory::Bubble

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.