In bash with `v=''`, the function `count() { echo $#; }` prints different numbers for `count $v` and `count "$v"`. What does each print, and why?
answer
- one variable does not mean one argument
- empty splits into no fields
- unset and empty behave alike unquoted
- quotes create the argument
- $# tells the two apart
basics
~20 scount $v prints 0 and count "$v" prints 1. An unquoted empty expansion is split on IFS into zero words, so no argument is passed at all, while double quotes force it to become exactly one argument, an empty string.
solid answer
~40 s`count $v` prints `0` and `count "$v"` prints `1`. Unquoted, the expansion produces the empty string, which word splitting then divides into zero words — with the default `IFS` there is nothing between the delimiters, so no argument reaches the function at all and the command line is one item shorter than you wrote. Quoted, splitting is suppressed and the result is exactly one argument that happens to be empty. The same is true for an *unset* variable: unquoted, both the empty and the unset case pass nothing. This is why an unquoted expansion in an argument list can shift every later argument into the wrong slot when a value turns out to be blank — a command like `mv $src $dst` with an empty `src` quietly becomes a one-argument `mv`.
code
bash · 8 linescount() { echo $#; }
v=''
count $v # 0 - the expansion produced zero words
count "$v" # 1 - quoting forces exactly one (empty) argument
unset u
count $u # 0 - unset behaves the same unquoted
count "${u:-x}" # 1 - and the argument is xgo deeper
Be able to say that count $v prints 0 and count "$v" prints 1, and that quoting is what guarantees exactly one argument even when the value is empty.
Explain the mechanism: word splitting over an empty result yields zero fields, so the argument disappears and later operands shift position, which is how mv $src $dst silently becomes a one-argument call.
Show how you prevent it in real scripts — quote every expansion, use ${var:-default} where blank is legitimate, validate required inputs up front, and know that erroring on unset variables does not cover set-but-empty.
Frame it as interface design for scripts: define which inputs may be empty, validate them at the boundary with clear failures, and pass argument lists as arrays so an optional value's absence is explicit rather than an accident of splitting.
## Zero words, not one empty word The rule this question tests is small and consequential: **an unquoted expansion is not guaranteed to produce one argument, and it can produce none.** After bash substitutes `$v`, the result is the empty string. Word splitting then runs over that result, looking for fields delimited by the characters in `IFS` (space, tab and newline by default). There is no text at all, so there are no fields, so zero words are produced — and a word that does not exist is not passed to the command. ```bash count() { echo $#; } v='' count $v # 0 count "$v" # 1 ``` Double quotes suppress splitting, so `"$v"` is guaranteed to be exactly one word even when that word is empty. This is a general property of quoting, not a special case for empty values. ## Unset behaves the same way ```bash unset u set -- $u; echo $# # 0 v='' set -- $v; echo $# # 0 set -- "$v"; echo $# # 1 ``` Unquoted, empty and unset are indistinguishable — both contribute nothing to the command line. Quoted, they are also indistinguishable, but now both contribute one empty argument. If you need to tell *unset* from *set-but-empty* you cannot do it by counting arguments; that distinction lives in the parameter expansion operators, where `${var-default}` and `${var:-default}` differ precisely on that point. ## Why the disappearing argument hurts The practical damage is **positional shift**. Commands identify their operands by position, so when one operand vanishes the ones after it slide forward into a different meaning: ```bash src='' # e.g. a lookup that returned nothing dst=/backup/ mv $src $dst # mv receives ONE argument: /backup/ ``` `mv` reports a usage error here, which is the lucky outcome. Other commands are less kind: an interpreter given one fewer argument may read from standard input and hang, and a command whose last argument is a directory may act on the wrong target. The same shape appears with `grep $pattern file` — an empty `pattern` makes `file` the pattern and leaves grep reading standard input. A related trap lives in conditional tests, where an unquoted empty variable removes an operand and changes which test the shell evaluates; the fix there is the same one word of quoting. ## The habits that avoid it - **Quote the expansion**: `mv -- "$src" "$dst"`. Now the empty value arrives as a real (empty) argument and the command can reject it with a clear error instead of misbehaving. - **Supply a default** where a blank is legitimate: `"${dir:-.}"` substitutes `.` when `dir` is unset or empty, so the argument is never blank. - **Validate early** rather than relying on the callee: check that a required value is non-empty and fail with your own message before building the command line. - **Fail on unset variables**: a strict-mode preamble makes referencing an undefined name an error, which catches the typo case — though not the set-but-empty case, which is exactly why the quoting habit still matters. ## Turning it around There is one place the behaviour is useful: an unquoted expansion that is empty *disappears cleanly*, which is occasionally used to make an optional flag vanish — `cmd $maybe_flag file`. It is a bad idiom in production scripts because it is indistinguishable from the bug, it still splits and globs any value it does have, and an array of arguments expresses "maybe empty list" honestly. Prefer building the argument list as an array. The one-sentence takeaway an interviewer wants: quotes are what turn a value into exactly one argument, and without them an empty value contributes nothing at all.
- Does an unset variable behave differently from an empty one here?Not for argument counting. Unquoted, both produce zero words; quoted, both produce one empty argument. The distinction between unset and set-but-empty only becomes visible through the parameter expansion operators, where the colon forms such as `${var:-default}` treat empty like unset and the colon-less forms do not.
- Show a realistic bug this causes.`mv $src $dst` with an empty `src` calls `mv` with a single argument, so the destination is read as the source and the command fails or acts on the wrong path. `grep $pattern file` with an empty pattern makes `file` the pattern and leaves grep reading standard input, so the script appears to hang. Quoting both expansions turns each into a clear error.
- Does enabling the shell's error-on-unset-variable option solve this?Only half of it. It aborts on a reference to a name that was never set, catching typos, but a variable that was explicitly assigned an empty string is perfectly defined and passes the check. Quoting the expansion, or supplying a default with `${var:-...}`, is what handles the set-but-empty case.
saying these in an interview costs you the question
- Thinks an unquoted empty variable passes one empty argument
- Assumes every expansion contributes exactly one argument
- Believes unset and empty differ in the unquoted argument count
- Says quoting an empty variable passes nothing