Variables in the shell are simpler than in most languages and easier to break, usually in the same line: name=value assigns, $name expands — and the space around the = is a bug, not style.
Assignment and expansion #
$ name="aospix"
$ echo "$name desktop"
aospix desktop
$ name = "aospix" # the space makes bash run a command named name
bash: name: command not found
No spaces around =. Names are letters, digits, and underscores, not starting with a digit. Read-only values get readonly, and unset name removes one.
Scope: shell, environment, child #
A variable exists in the current shell. Exporting it promotes it to the environment, where child processes inherit it:
$ APP_ENV=dev bash -c 'echo $APP_ENV' # not exported: empty
$ export APP_ENV=dev
$ bash -c 'echo $APP_ENV' # inherited: dev
That distinction is why scripts see some settings and not others, and it is the whole story behind PATH and tool configuration like EDITOR.
Parameter expansion: the free toolbox #
${...} is more than $name with braces — it is the shell's built-in string workshop:
| Form | Meaning |
|---|---|
${name:-fallback} | Use fallback if unset or empty |
${name:=fallback} | Assign fallback if unset or empty |
${name:?message} | Fail loudly if unset |
${#name} | Length |
${name%.txt} | Strip the shortest .txt suffix |
${name#./} | Strip a leading ./ |
${name/a/b} | Replace first a with b |
The default forms are the scripting workhorses — required-argument guards without an if:
config_file="${1:?usage: deploy.sh <config>}"
echo "deploying with $config_file"
The suffix strip is how bulk renames work: "${f%.jpeg}.jpg".
Special variables worth knowing #
$? is the last exit code, $1..$9 are script arguments, $# their count, $$ the current PID, and $0 the script's own name — the ingredients of every usage message.
Strings are only the start: arrays hold lists, and strict mode makes unset variables loud. The scripting series continues.
