File Size Statistics Script (Lua)

I used ChatGPT to write a script for generating a list of file statistics based on everything within the directory it is placed in. It uses LuaFilesystem, and generates a final output like the following after it’s done processing through the files:

2359    files found.
Average (mean) file size:       44842.524374735 bytes
Standard deviation:     320478.50592438
Multiple modes:
Mode 1: 126     bytes
Mode 2: 204     bytes
Frequency:      7
[####################] 0.00 - 199271.16: 2245 files
[##########          ] 199271.16 - 398542.33: 59 files
[#######             ] 398542.33 - 597813.49: 16 files
[#######             ] 597813.49 - 797084.65: 14 files
[#####               ] 797084.65 - 996355.82: 6 files
[#####               ] 996355.82 - 1195626.98: 8 files
[##                  ] 1195626.98 - 1394898.14: 2 files
[#                   ] 1394898.14 - 1594169.31: 1 files
[#                   ] 1594169.31 - 1793440.47: 1 files
[                    ] 1793440.47 - 1992711.63: 0 files
[                    ] 1992711.63 - 2191982.80: 0 files
[#                   ] 2191982.80 - 2391253.96: 1 files
[                    ] 2391253.96 - 2590525.12: 0 files
[                    ] 2590525.12 - 2789796.29: 0 files
[                    ] 2789796.29 - 2989067.45: 0 files
[##                  ] 2989067.45 - 3188338.61: 2 files
[                    ] 3188338.61 - 3387609.78: 0 files
[                    ] 3387609.78 - 3586880.94: 0 files
[                    ] 3586880.94 - 3786152.10: 0 files
[                    ] 3786152.10 - 3985423.27: 0 files
[                    ] 3985423.27 - 4184694.43: 0 files
[#                   ] 4184694.43 - 4383965.59: 1 files
[                    ] 4383965.59 - 4583236.76: 0 files
[                    ] 4583236.76 - 4782507.92: 0 files
[                    ] 4782507.92 - 4981779.08: 0 files
[                    ] 4981779.08 - 5181050.24: 0 files
[#                   ] 5181050.24 - 5380321.41: 1 files
[                    ] 5380321.41 - 5579592.57: 0 files
[                    ] 5579592.57 - 5778863.73: 0 files
[                    ] 5778863.73 - 5978134.90: 0 files
[                    ] 5978134.90 - 6177406.06: 0 files
[                    ] 6177406.06 - 6376677.22: 0 files
[#                   ] 6376677.22 - 6575948.39: 1 files
[                    ] 6575948.39 - 6775219.55: 0 files
[                    ] 6775219.55 - 6974490.71: 0 files
[                    ] 6974490.71 - 7173761.88: 0 files
[                    ] 7173761.88 - 7373033.04: 0 files
[                    ] 7373033.04 - 7572304.20: 0 files
[                    ] 7572304.20 - 7771575.37: 0 files
[                    ] 7771575.37 - 7970846.53: 0 files
[                    ] 7970846.53 - 8170117.69: 0 files
[                    ] 8170117.69 - 8369388.86: 0 files
[                    ] 8369388.86 - 8568660.02: 0 files
[                    ] 8568660.02 - 8767931.18: 0 files
[                    ] 8767931.18 - 8967202.35: 0 files
[                    ] 8967202.35 - 9166473.51: 0 files
[                    ] 9166473.51 - 9365744.67: 0 files
[                    ] 9365744.67 - 9565015.84: 0 files
[#                   ] 9565015.84 - 9764287.00: 1 files
0th percentile: 0       bytes
10th percentile:        167     bytes
20th percentile:        317     bytes
30th percentile:        476     bytes
40th percentile:        692     bytes
50th percentile (median):       986     bytes
60th percentile:        1428    bytes
70th percentile:        2101    bytes
80th percentile:        3650    bytes
90th percentile:        38917   bytes
100th percentile:       9764287 bytes

With minimal effort, you could change it quite a bit, because it’s written as pure functions. I wouldn’t have achieved this myself, nor produced it so quickly, if I didn’t have ChatGPT do the easy stuff for me. I found the experience quite helpful. While ChatGPT did once forget that Lua indexes tables starting with 1, and made a few weird decisions and downright inefficient code in some places, it allowed me to focus on making it work exactly how I wanted it to, instead of just mostly correct or “good enough for now”.

(Btw, the example output above is from my Obsidian vault. You can read a bit more about how I use Obsidian to organize my notes here.)

cc-pkg: A ComputerCraft Package Manager

(The latest information and a quick reference is located here.)

ComputerCraft is a Minecraft mod that adds Lua-based computers. Over time, many programs have been created, and several package managers have come and gone. As I write this, all that I have seen are gone – their original authors have moved on, and shut down the servers hosting packages.

Now it’s my turn to sell you a package manager. Unlike the others, I expect this to remain viable – even if I’m gone from the picture. If you want to skip to trying it, here’s how it’s installed (and how to ask for help):

pastebin get 9Li3u4Rc /bin/pkg
/bin/pkg help

(I’d also recommend installing the unix-like package, which adds /bin to your path, along with a few other small tweaks.)

Why is cc-pkg different?

  1. It is built on ComputerCraft’s pastebin integration.
  2. It does not require a maintainer.
  3. It is extremely simple – and flexible.

cc-pkg has the same three sub-commands of the default pastebin program: get, run, and put*. They each do exactly what you’d expect them to do, except they can use names as well as pastebin IDs. Names consist of alphanumeric characters and dashes, and there are two file types cc-pkg can recognize and utilize:

Both are plaintext lists of the form key=value. The first is called a package and has file paths (all starting with a forward slash) as keys, with names or pastebin IDs as values. This is how cc-pkg knows which files to download and where to put them. The second type is called a list, and contains names as keys, with names or pastebin IDs as values. Lists are saved to a local file cc-pkg uses to resolve package names – overwriting any existing entries with the same package name, which is how updating is done.

With just those core features, I think the system is viable.

*The put command is not implemented as of version 1.4.2 1.5.10, the latest at the time of writing finishing updating this introduction.

Command Extensions

On top of this, if a file is saved to /lib/pkg-commands/<name> and a user runs pkg <name>, that file will be run with the other arguments. This allows adding new functions, and overwriting the core functions to add additional features, if desired.

Under the Hood

cc-pkg keeps metadata through the following files:

  • /etc/pkg/names.list: The master names list. (How cc-pkg knows what to download.)
  • /etc/pkg/ids.list: Download history of cc-pkg. (An ordered list of every pastebin ID successfully downloaded.)
  • /etc/pkg/<package-name>: Each package is saved here by name, in addition to the files it specifies.

API Loadable

It uses global functions so that it can be loaded as an API to make a more advanced package management system on top of it – or just to make programs automatically download requirements using cc-pkg.

  • get(name_or_id, path): The core function that installs anything (package/list/file). (path is optional.)
  • down(id): Downloads from a pastebin ID, returning its content or throwing an error.
  • save(path, data): Write to file. (Note: This is not a binary write.)
  • append(path, data): Append to file. (Note: This is not a binary write.)
  • id(name_or_id): Recursively checks the master names list until a pastebin ID is returned. (Note: On failure, returns the last name resolved.)
  • type(data): Recognizes data as a package, list, or unknown type, and returns that type.
  • src(data): Combines this data with existing names (overwriting if duplicates exist). (If get is used, src will be called when needed automatically.)

An example of this is the pkg-search package, which adds a search command to look for specific package names within the master list.

Example Packages & Lists

  • A basic package looks like this:
    /startup=XsYUCf5b
    This is the multi-startup package, which makes the computer run all files in /.startup when started. It is required for several of my packages to function.
  • A package with a dependency looks like this:
    /etc/pkg/multi-startup=multi-startup
    /.startup/unix-like.lua=iG9idFgk

    This is the unix-like package, which requires multi-startup to function, and makes the computer operate more like a UNIX computer. (Due to how cc-pkg works, specifying to download a package anywhere will install that package. By convention and for consistency, I use the location cc-pkg installs packages by default.)
  • A “meta package” (a package that only lists dependencies) looks like this:
    /etc/pkg/ping=ping
    /etc/pkg/touch=touch
    /etc/pkg/which=which

    This is only part of the unix-meta package, which lists many packages that make a computer operate more like a UNIX computer. Packages are very flexible! They don’t even have to install anything themselves.
  • A list looks like this:
    pocket-computer-chat=qtnShai4
    pocketgps=8SXT1hDN

    This is the current list of pocket packages at the time of writing. (These can be added by running pkg get pocket-packages.) Remember, lists can reference other names (e.g. unix-stuff=unix-meta), allowing one to create aliases for existing packages & lists.