Basics

Guides

API Reference

Menu

Basics

Guides

API Reference

Aussom Scripting Toolkit Guide

This guide is a tour of the tools Aussom gives you for writing scripts: reading and writing files, running commands, reading environment variables, handling input and output, parsing arguments, formatting text, and talking to the network. It is task focused. For each job it shows the pieces you reach for and a short, working example.

This is the companion to scripting-usage.md. That guide explains how to run a script (the -s flag, the .auss extension, shebang lines, and args). This guide explains what you can do once the script is running. Every example here is written in script mode, so it is just top-level statements with no class or main.

A note on imports: most tools live in a module you pull in with include. The examples show the include lines they need. A few things are always available with no include: the string, list, and map methods, string.format, json, and the console object c.

The toolkit at a glance

Job Reach for Include
Print output, read input os.out, os.printf, os.readLine os
Parse command-line options cliparser cliparser
Read/write environment variables os.getEnv, os.setEnv os
Detect the platform os.isLinux, os.getOsType os
Run external commands os.run os
Set the exit code os.exit os
Read/write files file.read, file.readLines file
Build and inspect paths file.join, file.glob, file.walk file
Format and match text string.format, regex built-in, util
Parse and produce JSON json.parse, .toJson built-in
Make HTTP requests Http http
System facts, timing sys sys

1. Output and input

The os module carries the everyday input and output helpers. os.out writes a line to standard output. os.outErr writes a line to standard error, which is the right place for diagnostics so they do not mix into the data your script prints. os.printf writes a formatted string with no automatic newline, so the format string controls the line breaks.

include os;

os.out("starting");
os.printf("processed {} of {} files\n", 3, 10);
os.outErr("warning: skipped a bad row");

To read input, os.readLine returns one line without its trailing newline, or null at end of input. An optional prompt is written first. os.readAll reads everything from standard input, which is handy when your script is on the receiving end of a pipe.

include os;

name = os.readLine("Your name: ");
os.printf("Hello, {}!\n", name);

// Uppercase whatever is piped in: echo hi | ./shout.auss
text = os.readAll();
os.out(text.toUpper());

The console object c is also always available for logging with level tags (c.log, c.info, c.warn, c.err). Use os.out for the plain data your script produces and c for progress or log messages.

2. Command-line arguments

Every script receives args, a list of the strings that followed the script name on the command line. For simple scripts, read it directly.

include os;

if (#args < 1) {
    os.outErr("usage: greet <name>");
    os.exit(2);
}
os.printf("Hello, {}!\n", args.get(0));

When a script grows real options and flags, use the cliparser module instead of parsing args by hand. You declare the options, then parse. parseOrExit prints a usage message and exits on a bad command line, so you do not have to handle that yourself.

include os;
include cliparser;

opts = new CliOptions();
opts.value("o", "out", "Output file", true).argName("file");
opts.flag("v", "verbose", "Enable verbose output");

parsed = new CliParser().options(opts).parseOrExit(args, "build [options] <input>");

if (parsed.has("verbose")) { os.out("verbose mode on"); }
outFile = parsed.value("out");
inputs = parsed.args();
os.printf("writing {} from {} input(s)\n", outFile, #inputs);

3. Environment variables

os.getEnv reads a variable, with an optional default when it is not set. os.hasEnv tests for presence, and os.getEnvs returns the whole environment as a map.

include os;

home = os.getEnv("HOME", os.getEnv("USERPROFILE", "."));
if (os.hasEnv("DEBUG")) { os.out("debug is on"); }

os.setEnv records a variable that later os.run calls pass to the commands they start. Note that Aussom cannot change the JVM's own live environment, so setEnv affects the child processes your script runs and what os.getEnv reports back, but not unrelated code. That is exactly what a script needs when it sets up an environment for a command.

include os;

os.setEnv("BUILD_MODE", "release");
res = os.run("make");   // make sees BUILD_MODE=release

4. Platform and system facts

os.isWindows, os.isMac, and os.isLinux answer the common "which OS am I on" question, and os.getOsType returns "windows", "mac", or "linux" for a switch.

include os;

if (os.isWindows()) {
    os.run("cls");
} else {
    os.run("clear");
}
os.out("running on " + os.getOsType());

The sys module reports read-only facts about the machine and runtime: sys.getCurrentPath, sys.getHomePath, sys.getUserName, sys.getOsName/getOsVersion/getOsArch, sys.getFileSeparator, sys.getJavaVersion, and more. It also has sys.sleep(ms) and sys.getMills() for timing.

include sys;

os.out("cwd: " + sys.getCurrentPath());
os.out("user: " + sys.getUserName());
sys.sleep(500);   // pause half a second

5. Running external commands

os.run is the main way to shell out. Pass a string to run it through the platform shell, or a list to run a program directly with no shell. It returns a map with success (a bool), exitCode, stdout, and stderr.

include os;

res = os.run("ls -la /tmp");
if (res.success) {
    os.out(res.stdout);
} else {
    os.outErr("command failed with code " + res.exitCode);
}

An options map gives you control over the child process:

  • cwd sets the working directory.
  • env adds environment variables for the child.
  • stdin feeds a string to the child's standard input.
  • mergeStderr is true by default (stderr folds into stdout); set it false to capture stderr separately.
  • logToStdout mirrors the output to the console as it runs.
include os;

res = os.run("grep TODO", {
    "cwd": "src",
    "stdin": file.read("notes.txt"),
    "mergeStderr": false
});
os.out("matches:\n" + res.stdout);
if (res.stderr != "") { os.outErr(res.stderr); }

The list form is safer when arguments may contain spaces or shell characters, because nothing is passed through a shell to be re-parsed:

include os;

res = os.run(["git", "commit", "-m", "a message with spaces"]);

The older os.exec (string) and os.execRaw (list) still work and return a result key with the merged output; os.run is the newer, option-rich entry point.

6. Exit codes

A script exits 0 when it runs to the end and 1 if an uncaught exception escapes. To set a specific code yourself, call os.exit. This is how a script signals success or failure to whatever ran it, such as a shell or a CI job.

include os;

res = os.run("make test", {"mergeStderr": false});
if (!res.success) {
    os.outErr("tests failed (" + res.exitCode + "):");
    os.outErr(res.stderr);
    os.exit(1);
}
os.out("all good");

7. Files and directories

The file module reads and writes files and manages directories. file.read and file.write handle whole files as text. file.readLines returns a file as a list of lines, and file.writeLines writes a list back, with an append option.

include file;

file.write("greeting.txt", "hello\n");
whole = file.read("greeting.txt");

lines = file.readLines("data.csv");
file.writeLines("out.txt", ["first", "second"]);
file.writeLines("out.txt", ["appended"], true);   // append

For files too large to hold in memory, open a FileStream and process it in chunks:

include file;

fs = new FileStream();
fs.open("huge.log", "r");
while (!fs.eof()) {
    chunk = fs.read(8192);
    // process chunk ...
}
fs.close();

Directory and existence helpers round out the module: file.exists, file.isFile, file.isDir, file.mkdirs, file.ls (one level), file.rm, file.rmr (recursive), file.cp, and file.rename.

include file;

if (!file.exists("build")) { file.mkdirs("build"); }
file.cp("app.bin", "build/app.bin");

8. Building and finding paths

Join path parts with file.join, which uses the correct separator for the platform, so you never hand-build paths with slashes.

include os;
include file;

cacheDir = file.join([os.getTmpDir(), "myapp", "cache"]);
file.mkdirs(cacheDir);
dbPath = file.join([cacheDir, "data.db"]);

file.getExt and file.stripExt handle extensions, and file.getName and file.getParent split a path. All of them ignore a dot that appears in a parent directory name.

include file;

file.getExt("report.tar.gz");     // "gz"
file.stripExt("report.aus");      // "report"
file.getParent("/a/b/c.txt");     // "/a/b"

To find files, file.glob matches a pattern (a single star stays within one directory level; a double-star segment recurses), and file.walk lists every file under a directory.

include file;

sources = file.glob("src/*.aus");        // one level
allTests = file.glob("tests/**/*.aus");  // recursive
everything = file.walk("docs");          // every file under docs

For scratch space, file.tempFile and file.tempDir create unique temporary entries, and os.getTmpDir returns the system temp path.

include file;

work = file.tempDir("mytool");
file.write(file.join([work, "scratch.txt"]), "temporary");
// ... use it ...
file.rmr(work);

9. Text: formatting, splitting, and matching

string.format builds strings from a template. Placeholders are {} for the next argument, {N} for a positional argument, and {name} for a value from a map argument.

os.out("Hello {}, you have {} messages.".format("Sam", 4));
os.out("{0}/{1}/{0}".format("x", "y"));
os.out("{host}:{port}".format({ "host": "localhost", "port": 8080 }));

os.sprintf and os.printf are the same formatter with a C-style call: os.sprintf returns the string, and os.printf writes it to standard output.

include os;

line = os.sprintf("{} = {}", "count", 42);
os.printf("progress: {}%\n", 75);

The string type has the everyday methods a script needs: split, trim, toUpper/toLower, contains, startsWith/endsWith, indexOf, replace, and substr. A list is joined into a string with list.join.

fields = "name,age,city".split(",");     // a list of 3 strings
csv = ["a", "b", "c"].join(",");         // "a,b,c"
if (line.trim().startsWith("#")) { }     // a comment line

For pattern work, the regex module (in util) matches and replaces with Java regular expressions. regex.match returns a list of matches.

include util;

nums = regex.match("[0-9]+", "order 42, item 7");   // ["42", "7"]
clean = regex.replace("\\s+", " ", messyText);       // collapse spaces

10. Structured data: JSON

json.parse turns a JSON string into Aussom lists and maps, and any value's .toJson() method turns it back into a JSON string. Neither needs an include.

data = json.parse("{ \"name\": \"Bob\", \"tags\": [\"a\", \"b\"] }");
os.out("name: " + data.get("name"));
os.out("first tag: " + data.get("tags").get(0));

out = { "ok": true, "count": 3 };
file.write("result.json", out.toJson());

11. HTTP requests

The http module's Http client makes web requests. get, post, put, patch, and delete return a map with body (the response text) and info (a map with responseCode, isSuccessful, headers, and more).

include http;

client = new Http();
res = client.get("https://api.example.com/status");
if (res.info.isSuccessful) {
    data = json.parse(res.body);
    os.out("state: " + data.get("state"));
} else {
    os.outErr("request failed: " + res.info.responseCode);
}

A POST with a JSON body:

include http;

client = new Http();
res = client.post("https://api.example.com/items", "{ \"name\": \"widget\" }");
os.out("created, code " + res.info.responseCode);

12. Other utilities

A script often needs a few more common tools, all in the standard library:

  • math (include math): math.pi, math.sqrt, math.random, and the rest of the usual functions.
  • Dates: the Date type parses, formats, and does date arithmetic.
  • Encoding and hashing (include util): base64 and hex for encoding, uuid for unique IDs.
include util;

id = uuid.get();
encoded = base64.encode(someBuffer);

Putting it together

The pieces combine into real scripts. A few end-to-end patterns follow.

Process a text file line by line

Read a file, transform each line, and write the result.

include file;

lines = file.readLines("input.txt");
out = [];
for (i = 0; i < #lines; i = i + 1) {
    line = lines.get(i).trim();
    if (line != "" && !line.startsWith("#")) {
        out @= line.toUpper();
    }
}
file.writeLines("output.txt", out);

A build-and-check glue script

Run a command, check the result, and exit with a matching code.

include os;

projectDir = os.getEnv("PROJECT_DIR", ".");
res = os.run("make", { "cwd": projectDir, "mergeStderr": false });

if (res.success) {
    os.out("build succeeded");
} else {
    os.outErr("build failed (" + res.exitCode + "):");
    os.outErr(res.stderr);
    os.exit(1);
}

A small command-line tool

Parse options, do the work, and report cleanly.

include os;
include cliparser;
include file;

opts = new CliOptions();
opts.flag("u", "upper", "Uppercase the output");
parsed = new CliParser().options(opts).parseOrExit(args, "cat-up [options] <file>");

if (#parsed.args() < 1) {
    os.outErr("expected a file argument");
    os.exit(2);
}

text = file.read(parsed.args().get(0));
if (parsed.has("upper")) { text = text.toUpper(); }
os.out(text);

Fetch JSON from a URL

Get a resource, check the status, and parse the body.

include os;
include http;

res = new Http().get("https://api.example.com/user/1");
if (!res.info.isSuccessful) {
    os.outErr("fetch failed: " + res.info.responseCode);
    os.exit(1);
}
user = json.parse(res.body);
os.printf("user {} joined on {}\n", user.get("name"), user.get("joined"));

Cross-platform paths

Let file.join and the platform helpers handle the differences so the same script runs everywhere.

include os;
include file;

base = os.getEnv("APPDATA", os.getEnv("HOME", "."));
configDir = file.join([base, "mytool"]);
file.mkdirs(configDir);
configPath = file.join([configDir, "settings.json"]);
os.out("config at " + configPath);

See Also

  • scripting-usage.md - how to run and ship scripts: the -s flag, the .auss extension, shebang lines, argument passing, and exit codes.
  • design/scripting-improvement.md - the design behind the os and file scripting additions.
  • apac-usage.md - the apac package manager. Scripts can include apac-installed packages the same way they include the standard library.