Skip to content

Control Flow

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"
}

Iterate over lists, ranges, or chain results:

# over a list literal
for name in ["alice", "bob", "carol"] {
echo "hello $name"
}
# over a range
for i in 1..10 {
echo "iteration $i"
}
# over functional chain results
for 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}"
}
mut attempts = 0
while 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.

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 0
exit 42 # exit code 42
exit "fatal: missing config file" # prints to stderr, exits with code 1

exit propagates through loops and conditionals.

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 iterations
mut LASH_LOOP_LIMIT = 50000

In interactive mode, lash may prompt you to continue instead of erroring.

$? holds the exit code of the last executed command:

grep "needle" haystack.txt
if $? == 0 {
echo "found it"
} else {
echo "not found"
}

In functional chains, the exit code reflects the chain outcome.

Background a command with &:

make -j8 &
CommandDescription
jobsList all running and stopped background jobs
fg %nBring job n to the foreground
bg %nResume a stopped job in the background
Ctrl+ZSuspend the foreground process (SIGTSTP)
Ctrl+CInterrupt 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.