How to compare strings in Bash
Compare strings in Bash with [[ "$a" == "$b" ]], test for inequality, and sort strings lexically with < and >. Copy-paste examples with quoting rules.
Tldr
Use `[[ "$a" == "$b" ]]` for equality and `[[ "$a" != "$b" ]]` for inequality. Always quote both sides. Use `<` and `>` inside `[[ ]]` for lexical ordering (unquoted operators, since Bash needs to distinguish them from redirection). Avoid the single-bracket `[ "$a" = "$b" ]` unless you need POSIX portability.
Intro
String comparison is one of the most common sources of quoting bugs in Bash. This guide covers the modern [[ ]] syntax, the portable [ ] syntax, pattern matching, and lexical ordering.
Steps
Name
Compare for equality with [[ ]]
Text
The double-bracket [[ ]] is a Bash keyword, not a command, so it handles unquoted variables more safely — but you should still quote for clarity and to avoid pattern-matching surprises on the right-hand side.
Name
Compare for inequality
Text
Use != inside [[ ]] the same way as ==.
Name
Use = for POSIX-portable scripts
Text
The single-bracket [ ] test command is POSIX and works in dash/sh too, but requires strict quoting — an unquoted empty variable can turn into a syntax error.
Name
Do lexical ordering comparisons
Text
Inside [[ ]], < and > compare strings in the current locale's collation order. They must be unquoted operators (not escaped) or Bash will treat them as redirection inside [ ].
Name
Match against a pattern, not just literal equality
Text
The right-hand side of == inside [[ ]] is treated as a glob pattern when unquoted, which is powerful but easy to misuse. Quote it if you want a literal string match.
Faq
Q
What is the difference between == and = in Bash?
A
Inside [[ ]], == and = are equivalent for string equality. Inside the POSIX [ ] test, only = is portable; == works in Bash's [ ] as an extension but is not POSIX-compliant.
Q
Why did my string comparison silently do a pattern match?
A
Inside [[ ]], an unquoted right-hand operand after == or != is treated as a glob pattern. `[[ "$x" == *.log ]]` matches any string ending in .log, not the literal string "*.log". Quote the right side if you want a literal comparison.
Q
How do I compare strings case-insensitively?
A
Lowercase both sides first with parameter expansion: `[[ "${a,,}" == "${b,,}" ]]` (Bash 4+), or set `shopt -s nocasematch` to make == in [[ ]] case-insensitive for the current shell.