Control Flow
Conditionals
Section titled “Conditionals”Braces are required. No then/fi syntax:
if count > 10 { echo "many"} else if count > 0 { echo "some"} else { echo "none"}Conditions are evaluated in expression context, so bare names resolve as variables. To evaluate a command in a condition, wrap it in backticks:
if `grep -c "error" log.txt` > 0 { echo "errors found"}Boolean operators work as expected:
if verbose && count > 0 { echo "processing $count items"}
if !found || retries > 3 { exit "giving up"}For Loops
Section titled “For Loops”Iterate over lists, ranges, or chain results:
# over a list literalfor name in ["alice", "bob", "carol"] { echo "hello $name"}
# over a rangefor i in 1..10 { echo "iteration $i"}
# over functional chain resultsfor f in `ls`.filter(x => x.endsWith(".d")) { echo "compiling $f"}Object iteration destructures key-value pairs:
let config = {host: "localhost", port: "8080", proto: "https"}for key, value in config { echo "${key}=${value}"}While Loops
Section titled “While Loops”mut attempts = 0while attempts < 5 { echo "attempt $((attempts + 1))" attempts = attempts + 1}Pattern match with | for alternatives and _ as a wildcard:
let ext = "json"match ext { "d" | "di" => echo "D source" "json" => echo "JSON data" "yaml" => echo "YAML config" _ => echo "unknown: $ext"}If no branch matches and there is no _ wildcard, lash produces a runtime error.
Break and Continue
Section titled “Break and Continue”Both work in for and while loops:
for line in `cat results.csv` { if line.startsWith("#") { continue } if line.contains("STOP") { break } echo $line}Terminate a script immediately:
exit # exit code 0exit 42 # exit code 42exit "fatal: missing config file" # prints to stderr, exits with code 1exit propagates through loops and conditionals.
Loop Protection
Section titled “Loop Protection”lash prevents infinite loops. If a loop exceeds 1000 iterations with no variable mutations, it reports an error. Loops where variables are actively mutated are allowed to exceed the limit.
Configure the threshold with the LASH_LOOP_LIMIT environment variable:
# allow up to 50000 iterationsmut LASH_LOOP_LIMIT = 50000In interactive mode, lash may prompt you to continue instead of erroring.
Exit Codes
Section titled “Exit Codes”$? holds the exit code of the last executed command:
grep "needle" haystack.txtif $? == 0 { echo "found it"} else { echo "not found"}In functional chains, the exit code reflects the chain outcome.
Job Control
Section titled “Job Control”Background a command with &:
make -j8 &| Command | Description |
|---|---|
jobs | List all running and stopped background jobs |
fg %n | Bring job n to the foreground |
bg %n | Resume a stopped job in the background |
Ctrl+Z | Suspend the foreground process (SIGTSTP) |
Ctrl+C | Interrupt the foreground process (SIGINT) |
A backgrounded functional chain runs the entire chain in the background. When brought to foreground, buffered output is printed.
If a chain receives SIGINT, lazy evaluation stops immediately and partially consumed ranges are discarded.