Skip to content

Writing Scripts

lash scripts use the .lash file extension and are run with:

Terminal window
lash myscript.lash

No shebang needed, though you can add one if you want to make scripts directly executable:

#!/usr/bin/env lash
echo "hello from lash"

Declare expected arguments with a usage block. lash handles parsing, type validation, constraint checking, and --help generation for you:

usage {
Deploy a service to the specified environment.
---
service: string - Service name to deploy
env: string - Target environment [staging, production]
optional port: int = 8080 - Port to bind
optional dry_run: bool = false - Preview without deploying
}
echo "deploying $service to $env on port $port"
if dry_run {
echo "(dry run -- no changes made)"
}

The block has two sections separated by ---:

  1. Description — free-form text describing the script.
  2. Argument definitions — one per line.

Arguments without optional are positional and required. Arguments with optional are passed as --name value flags.

Terminal window
lash deploy.lash myapp staging
lash deploy.lash myapp production --port 9090
lash deploy.lash myapp staging --dry_run true
lash deploy.lash --help

--help auto-generates formatted help output from the usage block.

TypeDescriptionExample
stringArbitrary text"hello"
intInteger number42
floatFloating-point number3.14
boolBoolean flagtrue
semverSemantic version string"1.2.3"

Restrict argument values beyond type checking:

usage {
Process data files.
---
format: string - Output format [json, csv, xml]
quality: int - Quality level 1..100
name: string - Identifier /^[a-z][a-z0-9_]+$/
}
  • Enum: [val1, val2, val3] — value must be one of the listed options.
  • Regex: /pattern/ — value must match the pattern.
  • Range: min..max — numeric value must be in the inclusive range.

When arguments fail validation, lash produces human-friendly messages indicating which argument failed, the expected type or constraint, and what was provided.

VariableDescription
$0Path to the current script
$1-$NPositional arguments by index
$#Number of arguments passed
$@All arguments as a list
echo "script: $0"
echo "first arg: $1"
echo "total args: $#"
for arg in $@ {
echo " arg: $arg"
}

lash scripts can contain inline unit tests using unittest blocks. Tests are ignored during normal execution and only run with the --test flag.

fn add(a, b) {
a + b
}
fn clamp(val, lo, hi) {
if val < lo { lo }
else if val > hi { hi }
else { val }
}
# adds two positive numbers
unittest {
let result = add(2, 3)
result.must.equal(5)
}
# adds negative numbers
unittest {
add(-1, -2).must.equal(-3)
}
# clamps value above maximum
unittest {
clamp(150, 0, 100).must.equal(100)
}
# clamps value below minimum
unittest {
clamp(-5, 0, 100).must.equal(0)
}
# preserves value within range
unittest {
clamp(50, 0, 100).must.equal(50)
}

The comment immediately before a unittest block becomes the test name.

The .must property is available on any value and returns an assertion proxy:

AssertionDescription
.must.equal(expected)Value equality (string comparison)
.must.notEqual(expected)Value inequality
.must.beGreaterThan(expected)Numeric greater-than
.must.beLessThan(expected)Numeric less-than
.must.contain(value)String/list contains
.must.startWith(value)String starts with
.must.endWith(value)String ends with

Override the default failure message with .because("reason"):

result.must.equal(42).because("calculation should return 42")

Note that equal() uses string comparison. 5.must.equal(5.0) fails because "5" != "5.0". Compare values of the same type.

Terminal window
lash script.lash --test

Output:

[PASS] adds two positive numbers
[PASS] adds negative numbers
[PASS] clamps value above maximum
[PASS] clamps value below minimum
[PASS] preserves value within range
5 tests: 5 passed, 0 failed

A failed assertion stops the current test but does not abort remaining tests. Each unittest block runs in a fresh child scope — tests cannot affect each other.

Combine with --log-verbose to see test names printed before execution:

Terminal window
lash script.lash --test --log-verbose