Prefer [ p ] && [ q ] over [ p -a q ]

The `-a` and `-o` operators inside `[ ]` are deprecated by POSIX. Chain with `&&` and `||` instead.

Problem

POSIX explicitly declares `-a` (and) and `-o` (or) inside `[ ]` to have unspecified behavior, especially with arguments that look like operators. Shells differ on precedence and on how empty operands are handled. Chained `[ … ] && [ … ]` is fully defined.

Bad

if [ "$a" = 1 -a "$b" = 2 ]; then ...
if [ -f "$f" -o -d "$f" ]; then ...

Good

if [ "$a" = 1 ] && [ "$b" = 2 ]; then ...
if [ -f "$f" ] || [ -d "$f" ]; then ...

# Or use [[ ]] in Bash:
if [[ "$a" = 1 && "$b" = 2 ]]; then ...

Explanation

`[[ ]]` is a Bash builtin with first-class `&&`/`||`/`==` operators and predictable parsing. Use it in Bash scripts; use chained `[ ]` for POSIX-portable scripts.

When It Matters

The -a and -o operators inside [ ] are ambiguous by design: the test builtin has to guess whether an operand is data or an operator, and a value like "!" or "-f" changes the parse. POSIX marked them obsolescent for exactly this reason, and different shells resolve the ambiguity differently. A condition that works with ordinary inputs can therefore be flipped by a user-supplied string that happens to look like an operator.

Second Example

Note

Each [ ... ] is a separate command, so && and || sequence them with well-defined precedence — the shell, not the test builtin, does the joining.

Exceptions

None worth keeping. Every -a or -o can be rewritten mechanically as separate tests joined with && or ||, and the rewrite is both clearer and more portable.

Faq

Q

Is [[ ]] a full replacement?

A

In Bash, ksh, and zsh yes, and it is safer because it does not word-split or glob its operands. It is not available in POSIX sh.

Q

How do I group conditions?

A

Use { ...; } with && and || in sh, or parentheses inside [[ ]] in Bash: [[ ( -f $a || -f $b ) && -r $c ]].

Q

What about -a for "file exists"?

A

That is a different, unary -a and is also non-standard. Use -e for existence.