• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
NameHero® Blog

NameHero® Blog

Web Hosting Tips & Resources From NameHero

  • AI Agents
    • OpenClaw
    • Claude Code
    • n8n
    • Hermes Agent
    • Open WebUI
    • Docker
  • Hosting
    • Web Hosting
    • WordPress Hosting
    • WooCommerce Hosting
    • Enterprise Hosting
    • Email Hosting
    • HeroicGuard
    • GPU Hosting
    • Website Builder
  • VPS
    • Managed VPS
    • Unmanaged VPS
    • Flex VPS
  • Reseller
  • Gaming
  • Domains
  • Account
  • Blog Home
  • Categories
  • Authors

Bash Conditional Expressions: [[ vs [ vs test

Bhagwad Park

Published on: December 10, 2025

Categories: Linux Command Line, VPS Hosting 0

In bash scripts, we use conditional statements frequently. Whether it’s in an “if fi” logic block or within a “while” or “do” loop, we evaluate statements all the time to determine whether they’re true or false, and perform various actions based on the result. True to form, there are multiple ways to do this, and bash has evolved better methods over the years after the initial problems became apparent with the older methods. Here, we look at three conditional expressions – test, [, and [[. Of these, the first two are the same – [ is a syntactic shorthand for test, but [[ is the one that’s best used these days.

Let’s see the differences between them.

The Original Conditional Expression – test

This is the original command, and it was built into the shell in 1981. Using it, we can perform basic comparisons using the following syntax:

test EXPRESSION

If the expression evaluates to the truth, it returns an exit status of 0. If it’s false, it returns an exit status of 1. If there’s an error, it returns a status of 2. The “test” command allows several operators that allow us to compare numbers, strings, and get the status of files. For example, here’s a selection of representative operators:

-f file - Checks if the file exists
-d directory - Checks if a directory exists
-r file - Checks for a readable file
-z string - Checks if the string length is zero
-n string - Checks if the string length is non-zero
number -eq number - Checks if two numbers are equals
number1 -gt number2 - Checks if the first number is greater than the second
number1 -le number2 - Checks if the first number is lesser than or equal to the second

Some of these operators might be familiar to you. In an earlier article on NameHero, we’d talked about string comparison, and we’d mentioned the “-gt” operator to compare the length of strings.

Based on this, we can run the following commands in bash:

if test 3 -gt 2; then echo "Numeric Comparison"; fi
if ! -f nonexistentfile.txt; then echo "File doesn't exist"; fi

Here’s a screenshot of the commands:

Using Test Expressions

As you can see, the expressions are pretty simple, and with operators for numerics, strings, and files, it pretty much covers everything. As we’ll see further down, it has a number of quirks that make it dangerous to use, and this explains why it was superseded in the late 1990s.

But first, a quick note on the main alias of “test”.

The [] Conditional – A Syntactic Shorthand for “test”

The “test” expression by itself seems a bit clumsy. It’s not easy to demarcate visually where the expression starts and ends. Other programming languages encase their expressions in brackets, so to make it easier to parse expressions for those used to programming languages, the [] syntax was developed.

The following two expressions are the same:

test 3 -gt 2
[ 3 -gt 2 ]

So, if we replace it in our earlier command, we get this:

if [ 3 -gt 2 ]; then echo "Numeric Comparison"; fi

As you can see, this gives us the same output as the previous command:

Using [ Instead of Test

Indeed, the [] syntax is so much more popular, precisely because it resembles programming languages so much, and it’s easy to read. Today, hardly anyone uses the “test” command as a conditional expression.

But what we often miss is that both “test” and [] have hidden pitfalls that can generate hard-to-debug errors, and waste hours of your time until you realize what went wrong. Here are the problems.

Why “test” and [] Are Problematic

Both test and its “[]” equivalent can behave in unexpected ways when using and comparing variables within the conditional blocks. These have to do with quoting and strings separated with delimiters.

Word Splitting Problems

Let’s say you have a string in a variable and you want to compare it to another string. The following seems to be the right way to do it:

[ $var = "xyz" ]

But see what happens when we try to assign multiple words to the “var” variable:

Word Splitting
Word Splitting

What does bash mean by “too many arguments”? Why isn’t it working?

The reason is that when bash interprets the [] conditional block, the shell performs word splitting on the variables inside it before evaluating the condition. In other words, bash is expanding the variables. So a statement like:

[ $var = "hello" ]

Becomes:

[ hello world = "hello" ]

This conditional block has four separate arguments, instead of the expected three. Normally, the block expects two arguments to compare, and one argument for the operator. This one doesn’t make sense. The code doesn’t know what you’re trying to do, so and so it throws an error.

The way to fix this is by putting the variable in quotes. Like this:

[ "$var" = "hello" ]

Now, when we run it, we don’t get an error.

No Word Splitting with Quotes

Instead, the exit status of the command is 1 – meaning false, as expected.

But word splitting is just one of the problems when using test and [].

Glob Expansion

Along with variables, bash also expands globs inside these operators. Globs are pattern-matching strings, and we can use them in conditional expressions – but “test” and [] do not support pattern matching. Consider the following command:

[ *.txt = "file.txt" ]

In the first place, we’re not even sure what this expression is supposed to be doing. Is it trying to compare “file.txt” to the literal string “*.txt”? Is it trying to see if “file.txt” matches the “*.txt” pattern? Either way, it doesn’t work. Here’s the output:

Glob Expansion
Glob Expansion

What’s happening is that bash is expanding the glob “*.txt” to include all the text files in the directory. So the command becomes:

[ file1.txt file2.txt = "file.txt" ]

Which, as we’ve seen above, is simply too many arguments.

Other Issues

The problems with “test” and [] don’t stop there. There are all kinds of other inconveniences, such as being unable to use logical operators inside the brackets, and the need for spaces around the square brackets – good practice, but it should not be necessary.

The Solution? Use [[ ]]

The modern way to solve the above problem is to use the updated syntax [[ ]] – double square brackets, instead of single ones. Now all our above examples are valid commands:

Double Square Brackets
Double Square Brackets

At least these don’t give errors anymore. The second command is still problematic, because the user is likely trying to match a pattern, but bash treats “*.txt” as a string. The rules around this are complex, and I won’t go into them now. Suffice to say that the proper command for pattern matching is this:

[[ "file.txt" == *.txt ]]

Now we see it working with an exit status code of “0”, as expected:

How to do Pattern Matching

Because of this (and other) quirks, we always use double square brackets [[ ]] when evaluating conditional expressions in bash. It might have been easier to just change the definition of the single-bracket [] syntax, but that would have caused problems with legacy code, so they introduced this new syntax instead.

Conclusion

A lot of people still use single brackets – [] – for conditional expressions in bash. But these can cause unexpected problems with quotations, variable expansion, and globbing. Instead, the modern practice is to use double square brackets – [[ ]] – for conditions. This avoids all the problems with the old syntax and is much more robust. As a best practice, you should always use double square brackets in your code.

Bhagwad Park Profile Picture
Bhagwad Park

I’m a NameHero team member, and an expert on WordPress and web hosting. I’ve been in this industry since 2008. I’ve also developed apps on Android and have written extensive tutorials on managing Linux servers. You can contact me on my website WP-Tweaks.com!

Related Posts

Git Bisect: A Step-by-Step Tutorial

Git bisect is a useful tool that git provides, allowing you to narrow down the commit that introduced a bug in your project.

Bash Signal Handling with Trap: EXIT, ERR, INT

Bash in Linux allows us to set functions that run when certain signals are generated - either by the script, or the shell. Here's how.

Curl vs Wget: Choosing the Right Tool for the Job

Linux has two popular tools for getting data from third party websites. Here's the difference between curl and wget.

Echo vs Printf in Bash: What’s the Difference?

Echo and printf can both be used in bash for outputting text. However, echo is for quick output, whereas printf gives you more power.

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

Follow & Subscribe

Exclusive promos, content and more!


Most Popular Posts

NameHero’s Recommended WordPress Plugin and Theme Setup

WordPress Hosting vs. Web Hosting – What’s The Difference?

How To Increase The InnoDB Buffer Pool Size

How To Fix A Stuck All-in-One WP Migration Import

How To Add A Subdomain In Cloudflare

Top Categories

  • WordPress
  • WordPress Tutorials
  • OpenClaw Hosting
  • Enterprise Hosting
  • WooCommerce
  • Web Hosting
  • Resellers
  • Website Security
  • Website Development
  • Website Performance
  • VPS Hosting
  • SEO Tips
  • Announcements
  • Domain Registration
NameHero

NameHero® proudly provides web hosting to over 40,000 customers with 99.9% uptime to over 750,000 websites.

  • Master Card
  • Visa
  • American Express
  • Discover
  • Paypal
Products
  • Web Hosting
  • Managed VPS Hosting
  • Unmanaged VPS Hosting
  • Flex VPS Hosting
  • WordPress Hosting
  • WooCommerce Hosting
  • Reseller Hosting
  • Enterprise Hosting
  • GPU Hosting
  • Email Hosting
  • HeroicGuard
  • Domains
  • Website Builder
  • AI Agent Hosting
Help & Support
  • NameHero Blog
  • NameHero Gaming Blog
  • Support
  • Help Center
  • Migrations
  • Affiliates
  • Gaming Affiliates
  • Call 1-855-984-6263
Company
  • About Us
  • Contact Sales
  • Reviews
  • Uptime
  • We're Hiring

Copyright © 2026 Name Hero, LLC. All rights reserved.
NameHero® is a registered trademark.

  • Privacy Policy
  • Terms of Use
  • Acceptable Use Policy
  • Payment Policy
  • DMCA