Functions
aux4 provides built-in functions that can be used in your execute instructions to format and transform variable values.
if
The if function allows you to execute instructions based on variable conditions. It uses the if() syntax followed by && for the true case and || for the false case.
Check if variable exists
1...
2 {
3 "name": "has-name",
4 "execute": [
5 "if(name) && echo 'name exists' || echo 'name does not exist'"
6 ],
7 ...
8 }
9...1> aux4 has-name1name does not exist1> aux4 has-name --name=John1name existsCheck if variable is empty
Use == with no value after it to check if a variable is empty.
1...
2 {
3 "name": "is-empty",
4 "execute": [
5 "if(name==) && echo 'name is empty' || echo 'name is not empty'"
6 ],
7 ...
8 }
9...Check if variable has specific value
1...
2 {
3 "name": "is-john",
4 "execute": [
5 "if(name==John) && echo 'name is John' || echo 'name is not John'"
6 ],
7 ...
8 }
9...Check if variable equals another variable
1...
2 {
3 "name": "is-default",
4 "execute": [
5 "set:defaultName=John",
6 "if(name==$defaultName) && echo 'name is default' || echo 'name is not default'"
7 ],
8 ...
9 }
10...Check if variable is not equal
Use != to check inequality.
1...
2 {
3 "name": "is-not-default",
4 "execute": [
5 "set:defaultName=John",
6 "if(name!=$defaultName) && echo 'name is not default' || echo 'name is default'"
7 ],
8 ...
9 }
10...param
The param function formats a variable as a command-line parameter. If the variable has a value, it outputs --name 'value'. If the variable is undefined, it outputs nothing.
1...
2 {
3 "name": "print",
4 "execute": [
5 "log:cmd param(name) param(age)"
6 ],
7 "help": {
8 "variables": [
9 {
10 "name": "name",
11 "text": "the name"
12 },
13 {
14 "name": "age",
15 "text": "the age"
16 }
17 ]
18 }
19 }
20...1> aux4 print --name Joe --age 201cmd --name 'Joe' --age '20'param with alias
You can specify an alias for the parameter name using param(variable:alias):
1...
2 {
3 "execute": [
4 "log:cmd param(name:n) param(age:a)"
5 ],
6 ...
7 }
8...1> aux4 print --name Joe --age 201cmd --n 'Joe' --a '20'param with multiple values
For variables with multiple: true, use param(variable**) to output each value as a separate parameter:
1...
2 {
3 "execute": [
4 "log:cmd param(tag**)"
5 ],
6 "help": {
7 "variables": [
8 {
9 "name": "tag",
10 "text": "tags",
11 "multiple": true
12 }
13 ]
14 }
15 }
16...1> aux4 print --tag first --tag second1cmd --tag 'first' --tag 'second'params
The params function formats multiple variables as command-line parameters in a single call:
1...
2 {
3 "execute": [
4 "log:cmd params(name, age, city)"
5 ],
6 ...
7 }
8...1> aux4 print --name Joe --age 201cmd --name 'Joe' --age '20'Only defined variables are included in the output.
value
The value function outputs just the value without the parameter name:
1...
2 {
3 "execute": [
4 "log:cmd value(name)"
5 ],
6 ...
7 }
8...1> aux4 print --name Joe1cmd 'Joe'value with multiple values
For variables with multiple: true, use value(variable*) to get all values as a JSON array:
1...
2 {
3 "execute": [
4 "echo value(text*)"
5 ],
6 "help": {
7 "variables": [
8 {
9 "name": "text",
10 "text": "text values",
11 "arg": true,
12 "multiple": true
13 }
14 ]
15 }
16 }
17...1> aux4 process abc def1["abc","def"]values
The values function outputs multiple values separated by spaces:
1...
2 {
3 "execute": [
4 "log:cmd values(name, age, city)"
5 ],
6 ...
7 }
8...1> aux4 print --name Joe --age 201cmd 'Joe' '20' ''Unlike params, this outputs empty strings for undefined variables.
object
The object function creates a JSON object from the specified variables:
1...
2 {
3 "execute": [
4 "log:cmd object(name, age)"
5 ],
6 "help": {
7 "variables": [
8 {
9 "name": "name",
10 "text": "the name"
11 },
12 {
13 "name": "age",
14 "text": "the age"
15 }
16 ]
17 }
18 }
19...1> aux4 print --name Joe --age 201cmd {"age":"20","name":"Joe"}Only defined variables are included in the JSON object. Undefined variables are omitted.
Values are strings by default ("age":"20"). Give a variable a type — number, boolean or json — and object emits it typed ("age":20), so you rarely need a separate conversion step.
object with aliases
You can rename fields using variable:alias syntax, similar to param():
1...
2 {
3 "execute": [
4 "json:cat data.json",
5 "log:cmd object(response.name:name, response.age:age, response.address.city:city)"
6 ],
7 ...
8 }
9...1> aux4 print1cmd {"age":"20","city":"New York","name":"Joe"}Without aliases, nested fields use underscores: response.name becomes response_name. With aliases, you control the key names.
object with dynamic fields
You can use a variable to specify which fields to include:
1...
2 {
3 "execute": [
4 "log:cmd object($fields)"
5 ],
6 "help": {
7 "variables": [
8 {
9 "name": "name",
10 "text": "the name"
11 },
12 {
13 "name": "age",
14 "text": "the age"
15 },
16 {
17 "name": "city",
18 "text": "the city"
19 },
20 {
21 "name": "fields",
22 "text": "comma-separated list of fields"
23 }
24 ]
25 }
26 }
27...1> aux4 print --name John --age 23 --city NYC --fields 'name,age'1cmd {"age":"23","name":"John"}object with all parameters
Use * to include every current parameter. The parameters are spread directly into the object (each becomes its own field) — * is not a field name. You can combine it with named fields, e.g. object(id, *).
1...
2 {
3 "execute": [
4 "log:object(*)"
5 ],
6 "help": {
7 "variables": [
8 { "name": "name", "text": "the name" },
9 { "name": "age", "text": "the age" }
10 ]
11 }
12 }
13...1> aux4 print --name John --age 231{"age":"23","name":"John"}Note that * includes aux4's injected parameters too (such as aux4HomeDir and packageDir), so pick named fields instead when you only want specific values.
You can also give the wildcard an alias to nest all parameters under a single key (as a real object, not a string), and combine it with named fields:
1...
2 {
3 "execute": [
4 "log:object(name, *:others)"
5 ],
6 ...
7 }
8...1> aux4 print --name John --age 231{"name":"John","others":{"age":"23","name":"John", ...}}nvl
The nvl function returns the first non-null, non-empty value from a list of candidates. It accepts any number of variable names and quoted literal strings as fallbacks.
1...
2 {
3 "name": "show-avatar",
4 "execute": [
5 "log:avatar=nvl(response.avatar[0].path, defaultAvatar, '/images/default.png')"
6 ],
7 ...
8 }
9...It checks each candidate left to right:
- response.avatar[0].path — if the path exists and is non-empty, use it
- defaultAvatar — if this variable is set, use it
- '/images/default.png' — literal fallback (quoted with single or double quotes)
1...
2 {
3 "name": "greet",
4 "execute": [
5 "log:Hello, nvl(nickname, name, 'stranger')!"
6 ],
7 "help": {
8 "text": "greet someone",
9 "variables": [
10 {
11 "name": "name",
12 "text": "the name",
13 "default": ""
14 },
15 {
16 "name": "nickname",
17 "text": "optional nickname",
18 "default": ""
19 }
20 ]
21 }
22 }
23...1> aux4 greet --nickname Joe1Hello, Joe!1> aux4 greet --name John1Hello, John!1> aux4 greet1Hello, stranger!path
The path function builds an absolute filesystem path from one or more segments, resolving it against the current working directory. It replaces the common case "${x}" in /*) ACFG='${x}';; *) ACFG="$PWD/${x}";; esac shell idiom.
Segments are separated by /:
- a bare word is resolved as a variable value
- .. and . are kept as literal path segments
- a quoted string (single or double quotes) is a literal segment
A relative result is made absolute against the current directory, an already-absolute value is returned unchanged, and an empty result stays empty.
1...
2 {
3 "name": "sync",
4 "execute": [
5 "log:db=path(db) jobs=path(jobsPath)"
6 ],
7 "help": {
8 "text": "resolve paths",
9 "variables": [
10 { "name": "db", "text": "database file", "default": "local.db" },
11 { "name": "jobsPath", "text": "jobs directory", "default": ".jobs" }
12 ]
13 }
14 }
15...1> aux4 sync1db=/home/me/project/local.db jobs=/home/me/project/.jobsMultiple segments are joined into a single path. Quote fixed folder names, and use .. to walk up:
1...
2 {
3 "name": "backup-dir",
4 "execute": [
5 "log:path(dir/'backups'/name)"
6 ],
7 "help": {
8 "text": "build a backup path",
9 "variables": [
10 { "name": "dir", "text": "base directory", "default": "data" },
11 { "name": "name", "text": "backup name", "default": "shop" }
12 ]
13 }
14 }
15...1> aux4 backup-dir1/home/me/project/data/backups/shoparg
The arg function accesses positional arguments by index, without needing to define a variable with arg: true. Position 0 is the command name, 1 is the first argument, and so on. If the index is out of bounds, it returns an empty string.
1...
2 {
3 "name": "greet",
4 "execute": [
5 "echo arg(0) arg(1)"
6 ],
7 "help": {
8 "text": "greet someone"
9 }
10 }
11...1> aux4 greet hello1greet helloYou can use arg at any position and in any order:
1...
2 {
3 "execute": [
4 "echo arg(1) arg(0)"
5 ],
6 ...
7 }
8...1> aux4 greet hello1hello greetargs
The args function returns positional arguments as a JSON array. Use args(*) to get all arguments, or specify indices with args(0,1,2).
args with all arguments
1...
2 {
3 "name": "greet",
4 "execute": [
5 "echo args(*)"
6 ],
7 "help": {
8 "text": "greet someone"
9 }
10 }
11...1> aux4 greet hello world1["greet","hello","world"]args with specific indices
1...
2 {
3 "execute": [
4 "echo args(0,1)"
5 ],
6 ...
7 }
8...1> aux4 greet hello1["greet","hello"]args with single index
Even with a single index, args always returns a JSON array:
1...
2 {
3 "execute": [
4 "echo args(1)"
5 ],
6 ...
7 }
8...1> aux4 greet hello1["hello"]exists
The exists function checks if a file exists at the path stored in a variable. It resolves to a shell test [ -f 'path' ], so it works with && and || for branching.
1...
2 {
3 "name": "use-file",
4 "execute": [
5 "set:avatar=nvl(response.avatar[0].path, 'default.png')",
6 "exists(avatar) && cp ${avatar} output/ || echo file not found"
7 ],
8 ...
9 }
10...1> aux4 use-fileIf default.png exists:
1(file is copied)If it doesn't:
1file not foundNote: Since exists() resolves to a shell test, use shell commands (echo, cp, etc.) in the &&/|| branches, not aux4 executors like log:.
date, time, datetime, utc and epoch
These functions return the current date and time. With no argument they produce ISO output; pass a format string to customize it.
| Function | Default output | Description |
|---|---|---|
| date() | 2026-07-21 | ISO date (local) |
| time() | 15:04:05 | ISO time (local) |
| datetime() | 2026-07-21T15:04:05 | ISO date-time (local, no offset) |
| utc() | 2026-07-21T22:04:05Z | ISO date-time in UTC (Zulu) |
| epoch() | 1784665271 | Unix timestamp in seconds |
datetime() is local time with no offset; use utc() when you need an unambiguous UTC timestamp (for logs, APIs, or cross-machine comparisons). A single call captures one timestamp, so date() and time() used in the same command stay consistent.
1...
2 {
3 "name": "stamp",
4 "execute": [
5 "log:built on date() at time()"
6 ],
7 ...
8 }
9...1> aux4 stamp1built on 2026-07-21 at 15:04:05Custom formats
date(), time(), datetime() and utc() accept a format string built from moment-style tokens.
| Token | Meaning | Token | Meaning |
|---|---|---|---|
| YYYY / YY | 4- / 2-digit year | HH / H | hour 24h, padded / not |
| MMMM / MMM | month name / abbreviation | hh / h | hour 12h, padded / not |
| MM / M | month number, padded / not | mm / m | minute, padded / not |
| DD / D | day, padded / not | ss / s | second, padded / not |
| dddd / ddd | weekday name / abbreviation | A / a | AM-PM / am-pm |
| Z / ZZ | UTC offset ±HH:MM / ±HHMM |
Doubled tokens are zero-padded, single tokens are not. Any character that isn't a token (like -, :, /, , or T) is emitted verbatim. To emit token letters literally, wrap them in square brackets — e.g. date(YYYY-MM-DD[T]HH:mm) keeps the T literal instead of formatting it.
1...
2 {
3 "name": "release",
4 "execute": [
5 "log:date(MMM, DD, YY) at time(H:mm)"
6 ],
7 ...
8 }
9...1> aux4 release1Jul, 21, 26 at 15:04epoch units
epoch() returns seconds by default. Pass a unit to change the resolution — epoch(ms) for milliseconds or epoch(ns) for nanoseconds.
1...
2 {
3 "name": "id",
4 "execute": [
5 "log:req-epoch(ms)"
6 ],
7 ...
8 }
9...1> aux4 id1req-1784665271173data safety
date(), time(), datetime(), utc(), epoch() and uuid() are common names in SQL and other languages, so aux4 resolves them before variables are expanded. This means they only act on the text you write in the execute array — tokens that arrive at runtime inside a ${variable}, command output, or a file are left untouched, so data or SQL flowing through a variable is safe. (Formats must be literal too: date(${fmt}) does not expand the variable, since resolution happens first.)
To keep a call you author yourself literal — for example a database's own uuid() or date(column) in an inline query — escape it with a backslash, as described in Escaping functions.
uuid
The uuid function generates a UUID. It defaults to version 7 (time-ordered, RFC 9562), which sorts chronologically and works well as a database key. Pass uuid(4) for a classic random version 4. A fresh value is generated for every occurrence, so uuid() uuid() yields two distinct identifiers. As with the date functions above, it resolves only in authored execute text (not in interpolated data), and a backslash keeps it literal (see Escaping functions).
| Call | Result |
|---|---|
| uuid() | 019f8661-8b26-7a0c-9d10-501669d7aa67 (v7) |
| uuid(7) | version 7 (explicit) |
| uuid(4) | 27f0ae22-0961-4f34-8179-64bcf4e39dbc (v4) |
1...
2 {
3 "name": "new-record",
4 "execute": [
5 "set:id=uuid()",
6 "log:created record ${id}"
7 ],
8 ...
9 }
10...1> aux4 new-record1created record 019f8661-8b26-7a0c-9d10-501669d7aa67Escaping functions
Any function call can be kept literal by prefixing it with a backslash. aux4 strips the backslash and emits the call verbatim instead of resolving it — useful when the text is meant for something else, such as a database's own uuid() or date(column) in an inline query.
1...
2 {
3 "name": "recent",
4 "execute": [
5 "db query \"SELECT \\uuid(), \\date(created_at) FROM events\""
6 ],
7 ...
8 }
9...1SELECT uuid(), date(created_at) FROM eventsThis works for every function — \value(...), \values(...), \param(...), \params(...), \object(...), \nvl(...), \path(...), \if(...), \exists(...), \arg(...), \args(...), \date(...), \time(...), \datetime(...), \utc(...), \epoch(...) and \uuid().
Because a .aux4 file is JSON — and JSON also uses the backslash as an escape character — write two backslashes in the file (\\uuid()), which becomes \uuid() in the resolved command. Note that a quoted string does not protect a call: 'uuid()' still resolves (to an aux4 UUID inside the quotes); only the backslash does.
Working with Nested Data
All functions support nested data from JSON responses:
1...
2 {
3 "execute": [
4 "json:cat data.json",
5 "log:cmd params(response.name, response.age, response.address.city)"
6 ],
7 ...
8 }
9...Given data.json:
1{
2 "name": "Joe",
3 "age": 20,
4 "address": {
5 "city": "New York",
6 "state": "NY"
7 }
8}1> aux4 print1cmd --responseName 'Joe' --responseAge '20' --responseAddressCity 'New York'For values with nested data:
1...
2 {
3 "execute": [
4 "json:cat data.json",
5 "log:cmd values(response.name, response.age, response.address.city)"
6 ],
7 ...
8 }
9...1cmd 'Joe' '20' 'New York'