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.
| 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 |
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.
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);
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
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
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.
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");
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");
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);
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
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());
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);
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.Date type parses, formats, and does date arithmetic.util): base64 and hex for encoding,
uuid for unique IDs.include util;
id = uuid.get();
encoded = base64.encode(someBuffer);
The pieces combine into real scripts. A few end-to-end patterns follow.
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);
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);
}
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);
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"));
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);
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.