ShellCheck finds this before it runs
The unquoted variable behind this error is SC2086, the ShellCheck finding most scripts trip first. Paste any other code into the ShellCheck Error Decoder.
The script ran for weeks. Then the value it tests came back empty (an empty file, a failed $(…), a variable cron never set) and [ printed unary operator expected and returned 2. Inside an if that means the condition is false, so the script quietly took the other branch. The error names the operator, not the variable, so the line it points at looks fine.
Why Does an Empty Variable Break [ ]?
Reproduced here on 2026-10-03 (Kali, bash 5.3.15, ShellCheck 0.11.0) with count and name unset:
[ is a command, and its arguments go through word splitting like any other command's. An unquoted empty variable produces zero words, not one empty word, so [ $count -eq 1 ] arrives as [ -eq 1 ]. With only two arguments, test reads the first as a unary operator (like -f or -z). -eq is not one, so it fails.
A value with a space breaks the same line from the other side:
The Same Expression Under [, Quotes, [[ and set -u
| Expression (count unset) | Result | Exit |
|---|---|---|
[ $count -eq 1 ] | [: -eq: unary operator expected | 2 |
[ "$count" -eq 1 ] | [: : integer expected | 2 |
[[ $count -eq 1 ]] | false, no error | 1 |
[[ $count -eq 0 ]] | true, no error | 0 |
[ "${count:-0}" -eq 1 ] | false, no error | 1 |
bash -u: [ "$count" -eq 1 ] | count: unbound variable | 127 |
[ -n $count ] | true, no error | 0 |
The raw run behind the table:
Two rows are worse than the error. [[ $count -eq 0 ]] is true for an empty value, because inside [[ ]] arithmetic an empty string evaluates to 0. And [ -n $count ] is true when count is empty, because it collapses to [ -n ], a one-argument test that is true for any non-empty string, here the literal -n. Neither prints anything. set -u is the only form that names the variable.
How Do I Fix It?
Three fixes, in the order to reach for them:
Quoting is the minimum and works in every shell. [[ ]] is the default for bash scripts. A default or a :? check is what the script should have had all along, because both fixes above still let an empty value through to the comparison. Adding set -u (part of set -euo pipefail) catches the variables you forgot to check.
How Does ShellCheck Catch It?
SC2086 is "info" severity, which is why people filter it out. Do not: it is the warning for this exact bug. SC2070 is an error because [ -n $var ] is silently wrong, not loudly wrong. Run shellcheck in CI or a pre-commit hook and this error never reaches a server.
Prerequisites
bash (any version) and, for the checks, ShellCheck: apt install shellcheck, dnf install ShellCheck or brew install shellcheck. No root.
Frequently Asked Questions
What does unary operator expected mean in bash?
It means the [ (test) command received an operator such as -eq or = without the value that should come before it. That happens when the variable on the left was empty or unset and not quoted: bash removes the empty word during word splitting, so [ $count -eq 1 ] reaches test as [ -eq 1 ]. test then tries to read -eq as a unary operator like -f or -z, cannot, and exits with status 2.
Why does the error only happen sometimes?
Because it only fires when the variable is empty. A script that reads a value from a file, a command or an environment variable works every time the value is present and breaks the first time it is missing: an empty file, a failed command substitution, a variable cron does not set. That is why it often appears in cron or CI and never in testing. Quoting every variable inside [ ] makes the behaviour the same whether the value is there or not.
Should I use [ ] or [[ ]] in bash?
In a bash script, prefer [[ ]]. It does not word-split or glob-expand unquoted variables, supports && and || inside the brackets, and adds pattern matching with == and regex with =~. Use [ ] only when the script must run under plain sh (dash, busybox), and then quote every variable. Note that [[ $count -eq 0 ]] is true when count is empty, because an empty string evaluates to 0 in arithmetic, so [[ ]] removes the error but not the need to validate input.
What is the difference between unary operator expected and too many arguments?
They are the same bug with opposite inputs. An empty unquoted variable disappears and leaves [ with too few words: unary operator expected. A variable containing spaces, such as name="john smith", splits into two words and leaves [ with too many: too many arguments. Quoting the variable, or using [[ ]], fixes both.
How do I fix integer expected after quoting the variable?
[: : integer expected means the quoted variable is empty and -eq needs a number. Quoting made the error honest; now decide what an empty value should mean. Use "${count:-0}" if empty means zero, or test [ -n "$count" ] first and handle the missing value explicitly. For required values, : "${count:?count must be set}" at the top of the script stops it with a named error.
The safe bash script template puts set -euo pipefail and the :? checks at the top of every script for this reason, and The Production Bash Toolkit ships that template ShellCheck-clean.
Part of the bash snippets collection
Related Scripts
- Bash If Else Examples — every test operator, and which ones need quotes
- Bash Environment Variables —
${VAR:-default}and${VAR:?message}in depth - Fix "bash: command not found" — what an empty variable does when it is the command
- Bash Error Handling —
set -euo pipefail, including whatset -ucatches - Bash Error Messages, Decoded — this error and eight others, each with the one check that names the cause