A shell is infrastructure. The worst thing it can do is not crash, it is to carry on and hand you a plausible answer that is quietly wrong: a line split in half, a field truncated, a path shortened, a function that does not exist evaluating to the empty string. You do not find out at the time. You find out from the output, days later, if at all.
So FreSH has one rule about failure:
Never truncate silently. Grow, or say so.
Everything below follows from that.
Anything whose size comes from your data grows to fit it: input lines, records and fields in awk, command output, arrays, the line editor's buffer, the job table. There is no maximum line length, no maximum field count, no maximum number of background jobs.
Reading a line goes through one helper, read_line in src/util.c, which
returns the whole line however long it is. Every utility that reads lines uses
it, so grep, sort, tail, paste, diff, read and the rest all behave
the same on a 100KB line.
Some limits are structural rather than data-shaped. Those stay, and they say so on stderr with a non-zero exit status instead of doing something almost right:
| Limit | What happens |
|---|---|
| 32 pipeline stages | a pipeline cannot have more than 32 stages |
| 8 process substitutions in one command | named, with the substitution that did not fit |
PATH_BUF on a redirection target |
path is too long to redirect to |
| a temporary file that cannot be made | named, rather than skipped |
$((1 + ))and$((5 / 0))are errors, not0((...))reports a bad expression instead of just returning false${v:?message}prints the message and stops the script, which is the entire point of the form${v:offset:length}with an offset that is not an expression is an error, not "the whole string"- awk calling a function that does not exist is an error, not the empty string
- awk dividing by zero, opening an output it cannot write, or being handed more arguments than a function declares are all errors with status 2
- an awk program with a syntax error does not run at all
Not every empty result is a failure, and these are deliberate:
$missingis empty, andset -uis how you ask for an error instead$5in awk when there are three fields is empty, as in every awk- a glob that matches nothing stays as written, unless you set
nullglob
When you add a code path that can fail, pick one of two behaviours and never a third:
- make the thing big enough, or
- print what went wrong to stderr and return non-zero.
If you find yourself writing a fixed buffer with a snprintf whose result you
do not check, or a return "" on a path the caller cannot tell apart from a
real value, that is the bug. The tests in tests/ and the bash differential in
tests/diff exist to catch the rest.