Lexical Structure
Namespace Resolution
Section titled “Namespace Resolution”lash keeps commands and variables in two separate namespaces. Which namespace a name belongs to depends on where it appears in your code:
-
Commands — the first word of a line is always looked up as a command. This includes built-in methods (like
sort), your own functions, and programs installed on your system (likegreporgit).echo "hello" # echo is a commandsort data.txt # sort is a built-in methodgit status # git is a program from $PATH -
Variables — everywhere else (right side of
=, inside conditions, etc.) names are looked up as variables.let name = "world" # name is a variableecho "hello $name" # $name reads the variableif name == "world" { echo "hi" } # name in a condition is a variable
When lash looks up a command, it checks in this order:
- Built-in method (e.g.,
sort,filter,map) - A function you defined
- A program from your
$PATH
Because commands and variables live in separate namespaces, they can share the same name without conflicting:
let grep = "my pattern"grep "foo" file.txt # runs /usr/bin/grep (command)let result = grep # reads the variableecho "pattern: $grep" # also reads the variableTo run a command where lash expects a variable, wrap it in backticks (see Functional Chains).
Quoting and Escaping
Section titled “Quoting and Escaping”lash supports three quoting styles:
Double Quotes
Section titled “Double Quotes”String with interpolation:
echo "hello $name"echo "hello ${name}"echo "result: ${count + 1}"echo "$parts[0]"echo "literal \$sign"Single Quotes
Section titled “Single Quotes”Literal string, no processing:
echo 'no $interpolation'echo 'no \escapes'Backticks
Section titled “Backticks”Command execution and functional bridge (not POSIX command substitution):
let files = `ls -la``ls -la`.sort().take(5)Escape Sequences
Section titled “Escape Sequences”Available inside double-quoted strings only:
| Sequence | Meaning |
|---|---|
\n | Newline |
\t | Tab |
\r | Carriage return |
\\ | Literal backslash |
\$ | Literal dollar sign |
\" | Literal double quote |
Bare Words
Section titled “Bare Words”In command position, unquoted words are literal arguments. In expression context, unquoted words resolve as variable names.
Command Output in Strings
Section titled “Command Output in Strings”Backticks capture command output. To embed command output in a string, assign to a variable first:
let today = `date`.trim()echo "today is $today"let branch = `git branch --show-current`.trim()Environment Variable Prefixes
Section titled “Environment Variable Prefixes”Uppercase names before a command set environment variables for that command only:
DFLAGS="-O2" dub build --compiler=ldc2CC=gcc CFLAGS="-Wall" makeOnly names matching [A-Z][A-Z0-9_]* are recognized as env var prefixes. Lowercase names are treated as commands.
Brace Expansion
Section titled “Brace Expansion”Unquoted brace patterns expand into multiple arguments at tokenization, before variable expansion runs.
| Form | Expansion |
|---|---|
{a,b,c} | a b c |
pre{a,b}post | prea preb postb (prefix + suffix) |
{a,b}{x,y} | ax ay bx by (Cartesian product) |
{1..5} | 1 2 3 4 5 |
{5..1} | 5 4 3 2 1 (descending) |
{0..10..2} | 0 2 4 6 8 10 (with step) |
{a..e} | a b c d e (single-letter range) |
{a,{b,c}} | a b c (nested) |
A pattern with fewer than two comma-separated alternatives and no range is left literal (matching bash). Single-quoted arguments pass through untouched. Use \{ / \} to escape literal brace characters.
mkdir -p src/{lib,bin,test}echo file{1..3}.txtcp main.{c,h} backup/Globbing
Section titled “Globbing”| Pattern | Matches |
|---|---|
* | any sequence in a single path component |
? | any single character |
[abc] | any one of the bracketed characters |
** | zero or more path components (recursive descent) |
**/*.d matches every .d file at any depth below the working directory. Hidden entries (leading .) are excluded unless the pattern explicitly starts with ..
ls source/**/*.d # every .d file under source/rm -f build/**/*.tmp # recursive cleanupPOSIX Parameter Expansion
Section titled “POSIX Parameter Expansion”Inside double-quoted strings and command arguments, ${...} accepts these bash-compatible forms:
| Form | Result |
|---|---|
${VAR:-default} | VAR if set and non-empty, otherwise default |
${VAR:+alt} | alt if VAR is set and non-empty, otherwise empty |
${VAR:?msg} | VAR if set and non-empty; otherwise emits msg to stderr |
${#VAR} | character length of VAR (0 if unset) |
${VAR/pat/rep} | VAR with the first pat replaced by rep |
${VAR//pat/rep} | VAR with every pat replaced by rep |
${VAR/pat/} | delete the first match of pat |
The default and alt branches are themselves expanded:
let host = "${SERVER:-localhost}"echo "${file/.txt/.bak}"echo "${PATH//:/\\n}"${ ... + 1 } and other lash expressions remain available without the leading sigil — "${count + 1}" is a lash arithmetic expression, not a POSIX form.