Version: 1.0 Date: 2024-01-15 Applies to: KaririCode\Dotenv 4.0+
This specification defines the .env file syntax supported by KaririCode\Dotenv. The syntax is a superset of POSIX shell variable assignment, compatible with Docker .env files and Bash variable declarations.
Files must be UTF-8 encoded. BOM (Byte Order Mark) is not supported. Line endings are normalized: CRLF (\r\n) and CR (\r) are converted to LF (\n) before parsing.
Empty lines and lines containing only whitespace are ignored.
Lines where the first non-whitespace character is # are comments and are ignored entirely.
# This is a comment
# This is also a comment (leading whitespace allowed)NAME=VALUE
The = separator is required. Whitespace around = is stripped from the name (right side) and value (left side):
FOO = bar # name="FOO", value="bar"
FOO =bar # name="FOO", value="bar"
FOO= bar # name="FOO", value="bar"A line with a valid name but no = is treated as an empty string assignment:
FOO # name="FOO", value=""The export keyword is silently stripped:
export FOO=bar # equivalent to FOO=barPattern: [A-Za-z_][A-Za-z0-9_.]*
Valid: FOO, foo_bar, App.Config, _PRIVATE, DB_HOST_1
Invalid: 1FOO (starts with digit), FOO-BAR (contains hyphen), FOO BAR (contains space)
Pattern: [A-Z][A-Z0-9_]*
Valid: FOO, DB_HOST, APP_ENV_1
Invalid: foo (lowercase), _FOO (starts with underscore), App.Config (dots and lowercase)
Strict mode enforces the POSIX convention for environment variable names.
Everything after = until the end of line, with:
- Trailing inline comments stripped:
FOO=bar # comment→"bar" - Leading and trailing whitespace trimmed
- Variable interpolation applied (§6)
FOO=hello world # "hello world"
FOO=hello world # note # "hello world"Delimited by "...". Support:
- Escape sequences (§5.4)
- Variable interpolation (§6)
- Multiline values (§5.5)
FOO="hello world" # "hello world"
FOO="hello # world" # "hello # world" (# is not a comment inside quotes)Delimited by '...'. Contents are literal — no escape processing, no interpolation.
FOO='hello $WORLD' # "hello $WORLD" (literal dollar sign)
FOO='hello\nworld' # "hello\nworld" (literal backslash-n)| Sequence | Result |
|---|---|
\n |
Newline (LF) |
\r |
Carriage return (CR) |
\t |
Tab |
\" |
Literal double quote |
\\ |
Literal backslash |
\$ |
Literal dollar sign (suppresses interpolation) |
\x (other) |
Literal \x (unknown escapes pass through) |
Double-quoted values can span multiple lines. The newlines are preserved literally:
RSA_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"Single-quoted values cannot span multiple lines — an unterminated single quote on the same line is a parse error.
FOO= # "" (empty string)
FOO="" # "" (empty string)
FOO='' # "" (empty string)Interpolation is applied in unquoted and double-quoted values. Single-quoted values are never interpolated.
Variables are resolved against:
- Already-parsed variables in the current file (top-to-bottom)
$_ENV$_SERVER- Empty string (if unresolved)
FOO=${BAR} # Value of BAR
FOO=${BAR:-default} # BAR if set and non-empty, else "default"
FOO=${BAR:+alternate} # "alternate" if BAR is set and non-empty, else ""FOO=$BAR # Value of BAR (terminated by non-alphanumeric/non-underscore)
FOO=$BAR_BAZ # Value of BAR_BAZ (underscores are part of the name)| Operator | BAR is set and non-empty | BAR is unset or empty |
|---|---|---|
${BAR} |
Value of BAR | "" |
${BAR:-default} |
Value of BAR | "default" |
${BAR:+alternate} |
"alternate" |
"" |
Nested interpolation within operator operands is not supported:
# NOT supported:
FOO=${BAR:-${BAZ}} # The operand is the literal string "${BAZ}"This matches the behavior of simple shell implementations. For complex defaults, use multiple lines:
BAZ_DEFAULT=fallback
FOO=${BAR:-${BAZ_DEFAULT}} # Still not supported; use:
# BAZ=fallback
# FOO=${BAR:-fallback}Comments starting with # preceded by whitespace are stripped from unquoted values only:
FOO=bar # comment # "bar"
FOO="bar # not comment" # "bar # not comment"
FOO='bar # not comment' # "bar # not comment"
FOO=bar#notacomment # "bar#notacomment" (no preceding whitespace)| Condition | Exception |
|---|---|
| Invalid variable name | ParseException::invalidVariableName() |
| Unterminated double quote | ParseException::unterminatedQuote() |
| Unterminated single quote | ParseException::unterminatedQuote() |
All exceptions include the line number and file path for diagnostics.
file = { line LF }
line = empty | comment | assignment
empty = [ WS ]
comment = [ WS ] "#" TEXT
assignment = [ "export" WS ] NAME [ WS ] "=" [ WS ] value
NAME = [A-Za-z_] [A-Za-z0-9_.]*
value = double_quoted | single_quoted | unquoted
double_quoted = '"' { DQ_CHAR | escape | interpolation } '"'
single_quoted = "'" { SQ_CHAR } "'"
unquoted = { UQ_CHAR | interpolation } [ WS "#" TEXT ]
escape = "\" ( "n" | "r" | "t" | '"' | "\" | "$" | ANY )
interpolation = "$" NAME | "${" NAME [ ( ":-" | ":+" ) OPERAND ] "}"