In a bash script, the line `count = 5` fails with `count: command not found`. Why does bash reject spaces around `=` in an assignment, and what are the correct forms when the value itself contains spaces?
answer
- the shell sees words, then a command
- = has to touch both sides
- spaces turn it into a command call
- the right side is a single word
- a space before 5 makes 5 the command
basics
~20 sBash parses count = 5 as the command count with the arguments = and 5. An assignment must have no spaces around =: write count=5, and quote the value when it contains spaces, as in msg="hello world".
solid answer
~40 sBash is a command language, not an expression language. Before anything else, it splits the line into words on whitespace and treats the first word as a command name. An assignment is recognised only when the very first word matches the shape `NAME=value` with no space on either side of the `=`, so `count = 5` is parsed as running the command `count` with the arguments `=` and `5` — hence `command not found`. The correct form is `count=5`. Because the right-hand side is still a single word, a value containing spaces must be quoted: `msg="hello world"`. Two near misses behave differently and are worth knowing: `count =5` still runs the command `count`, while `count= 5` sets `count` to the empty string just for the command `5`, which then fails to run.
code
bash · 15 lines#!/usr/bin/env bash
count=5
echo "count is $count"
msg="hello world"
echo "msg is [$msg]"
# these three do NOT assign what you expect:
# count = 5 -> runs the command 'count' with args '=' and '5'
# count =5 -> runs the command 'count' with arg '=5'
# count= 5 -> sets count empty for one command, then runs '5'
empty=
unset gone
echo "empty is set to [${empty}]"go deeper
Be able to say instantly that bash assignments take no spaces around =, and that a value containing spaces must be quoted. Write count=5 and msg="hello world" without hesitating.
Explain the parsing rule behind the error: bash splits the line into words and only a leading NAME=value word counts as an assignment, so count = 5 becomes a command invocation. Know that count= 5 is a legal prefix assignment, not a syntax error.
Talk about the failure mode rather than the syntax — a missed assignment leaves the variable unset, and an empty expansion later can produce a destructive command. Point at strict mode and linting as the systematic defence instead of code review vigilance.
Frame it as why shell needs mandatory tooling: the language has no compile step and its parser silently reinterprets a typo as a valid command. Argue for a linted, reviewed shell baseline, and for a rule about when a script has outgrown bash entirely.
## Bash parses words, not statements Most languages have a grammar in which `=` is an operator and surrounding whitespace is irrelevant. Bash does not. A bash line is first split into *words* on unquoted whitespace, and the shell then decides what kind of command it is looking at based on the shape of those words. The rule for a variable assignment is narrow: the word must match `NAME=word`, where `NAME` is a valid identifier (letters, digits and underscores, not starting with a digit) and the `=` is part of the same word. Write `count = 5` and you have produced three words: `count`, `=`, and `5`. None of them is an assignment, so bash falls back to the ordinary rule — the first word is a command name and the rest are its arguments. There is no program called `count` on a typical system, so you get `count: command not found` with exit status 127. ## The correct forms ```bash count=5 # integer-looking value, one word, fine unquoted msg="hello world" # value has a space, so it must be quoted path=/usr/local/bin # slashes are fine, no quoting needed empty= # sets empty to the empty string (it IS set) ``` The right-hand side of an assignment is a single word after quote removal. `msg=hello world` does not assign two words to `msg`; it assigns `hello` to `msg` *for the duration of the command `world`*, and then tries to run `world`. Quoting is what keeps the value together. One thing that surprises people coming from other shells or languages: you never put a `$` on the left. `$count=5` expands `count` first and then tries to run the resulting text as a command. The `$` is for *reading* a variable; a bare name is for *writing* it. ## The three near misses, precisely | Line | What bash actually does | |---|---| | `count=5` | Assigns `5` to `count` in the current shell | | `count = 5` | Runs the command `count` with arguments `=` and `5` | | `count =5` | Runs the command `count` with the argument `=5` | | `count= 5` | Sets `count` to empty for one command, then tries to run `5` | That last row is the interesting one, because it is not an error in the parser at all — it is the legal *prefix assignment* form, where one or more `NAME=value` words in front of a command apply to that command only. `count= 5` is a perfectly well-formed command line; it just names a command, `5`, that does not exist. ## Why this trips people up in reviews The failure mode is quiet. Because the shell sees a command rather than an assignment, the script keeps going with the variable unset, and the symptom shows up much later as an empty expansion — a `rm -rf "$prefix/data"` that becomes `rm -rf /data`, or a curl to `https:///api`. If the script runs with `set -u`, the later read of the unset variable at least aborts; without it, an empty value silently propagates. This is exactly the class of bug linters catch cheaply, and ShellCheck flags the pattern rather than waiting for runtime. ## Assignment versus other things that look like it The same no-spaces rule applies anywhere bash expects a `NAME=value` word, and the error messages differ: ```bash export PATH = /usr/bin # export: `=': not a valid identifier export PATH=/usr/bin # correct declare -i n = 5 # declare: `=': not a valid identifier declare -i n=5 # correct ``` `export`, `declare`, `readonly` and `local` are builtins that take `NAME` or `NAME=value` *arguments*. Splitting the argument with spaces hands them three separate arguments, and `=` on its own is not a legal identifier. ## Set versus unset versus empty Finally, `empty=` and `unset empty` are not the same thing. After `empty=`, the variable exists and holds the empty string; after `unset empty`, the name is gone. Under `set -u` the first is a perfectly valid read and the second aborts the script, and the parameter-expansion operators that distinguish the two cases exist precisely because the distinction is real.
- What is the difference between `count =5` and `count= 5`?`count =5` is still a command invocation: bash runs `count` with the single argument `=5`. `count= 5` is a valid prefix assignment — it sets `count` to the empty string in the environment of the command that follows, and that command is `5`, which does not exist. The first fails on the name `count`, the second on the name `5`.
- Why does `export PATH = /usr/bin` also fail?`export` takes arguments of the form `NAME` or `NAME=value`. Splitting on spaces hands it three arguments: `PATH`, `=` and `/usr/bin`. Bash exports `PATH` unchanged, then rejects `=` and `/usr/bin` because neither is a valid identifier. The fix is the same as for a plain assignment: `export PATH=/usr/bin`, with no spaces.
- Is `x=` the same as `unset x`?No. `x=` leaves `x` defined and holding the empty string, so under `set -u` reading `$x` is fine. `unset x` removes the name entirely, and reading it under `set -u` aborts the script. Code that tests whether a value was supplied at all has to distinguish the two cases, not just check for emptiness.
saying these in an interview costs you the question
- Says bash allows spaces around = like Python
- Writes $count=5 with a dollar sign on the left
- Assigns a multi-word value without quoting it
- Thinks the error means the variable name is invalid
- Believes x= and unset x mean the same thing