Single and Double Brackets

Bash provides the test command. This lets you test logical expressions. The expression will return an answer that indicates a true or false response. A true response is indicated by a return value of zero. Anything other than zero indicates false.

Chaining commands on the command line with the && operator uses this feature. Commands are only executed if the previous command completes successfully.

If the test is true, the word “Yes” will be printed.

The single bracket conditional tests mimic the test command. They wrap the expression in brackets “[ ]” and operate just like the test command. In fact, they’re the same program, created from the same source code. The only operational difference is how the test version and the [ version handle help requests.

This is from the source code:

We can see the effect of this by asking test and [ for help and checking the response code sent to Bash.

Both test and [ are shell builtins, meaning they are baked right into Bash. But there’s also a standalone binary version of [ .

By contrast, the double bracket conditional tests [[ and ]] are keywords. [[ and ]] also perform logical tests, but their syntax is different. Because they’re keywords, you can use some neat features that won’t work in the single bracket version.

The double bracket keywords are supported by Bash, but they’re not available in every other shell. For example, the Korn shell does support them, but the plain old shell, sh, doesn’t. All of our scripts start with the line:

This ensures we’re calling the Bash shell to run the script.

RELATED: How to Create and Run Bash Shell Scripts on Windows 10

Builtins and Keywords

We can use the compgen program to list the builtins:

Without piping the output through fmt we’d get a long list with each builtin on its own line. It’s more convenient in this instance to see the builtins grouped together into a paragraph.

We can see test and [ in the list, but ] isn’t listed. The [ command looks for a closing ] to detect when it has reached the end of the expression, but ] is not a separate builtin. It’s just a signal we give to [ to indicate the end of the parameter list.

To see the keywords, we can use:

The [[ and ]] keywords are both in the list, because [[ is a one keyword and ]] is another. They are a matched pair, just like if and fi , and case and esac .

When Bash is parsing a script—or a command line—and detects a keyword that has a matching, closing keyword it gathers everything that appears between them and applies whatever special treatment the keywords support.

With a builtin, what follows the builtin command is passed to it exactly like parameters to any other command-line program. This means special care has to be taken by the author of the script regarding such things as spaces in variable values.

Shell Globbing

Double bracket conditional tests can make use of shell globbing. This means the asterisk “*” will expand to mean “anything.”

Type or copy the following text into an editor and save it to a file called “whelkie.sh.”

To make the script executable we’ll need to use the chmod command with the -x (execute) option. You’ll need to do this to all of the scripts in this article if you want to try them out.

When we run the script we see the string “elk” was found in the string “Whelkie”, regardless of what other characters surround it.

One point to note is that we don’t wrap the search string in double quotes. If you do, the globbing won’t happen. The search string will be treated literally.

Other forms of shell globbing are allowed. The question mark “?” will match single characters, and single square brackets are used to indicate ranges of characters. For example, if you don’t know which case to use, you can cover both eventualities with a range.

Save this script as “damme.sh” and make it executable. When we run it the conditional statement resolves to true, and the first clause of the if statement is executed.

Quoting Strings

We mentioned wrapping strings in double quotes earlier. If you do, shell globbing won’t occur. Although convention says it is good practice, you don’t need to wrap string variables in quotes when using [[ and ]]even if they contain spaces. Look at the next example. Both the $stringvar and $surname string variables contain spaces, but neither one is quoted in the conditional statement.

Save this into a file called “surname.sh” and make it executable. Run it using:

Despite both strings containing spaces, the script succeeds and the conditional statement resolves to true. This is useful when dealing with paths and directory names that contain spaces. Here, the -d option returns true if the variable contains a valid directory name.

If you change the path in the script to reflect a directory on your own computer, save the text into a file called “dir.sh” and make it executable, you can see that this works.

RELATED: How to Work with Variables in Bash

Filename Globbing Gotchas

An interesting difference between [ ] and [[ ]] relates to file names with globbing in them. The form “*.sh” will match all script files. Using single brackets [ ] fails unless there is a single script file. Finding more than one script throws an error.

Here’s the script with single bracket conditionals.

We saved this text into “script.sh” and made it executable. We checked how many scripts were in the directory, then ran the script.

Bash throws an error. We removed all but one script file and ran the script again.

The conditional test returns true and the script doesn’t cause an error. Editing the script to use double brackets provides a third type of behavior.

We saved this into a file called “dscript.sh” and made it executable. Running this script in a directory with many scripts in it doesn’t throw an error, but the script fails to recognize any script files.

The conditional statement using double brackets only resolves to true in the unlikely case that you have a file actually called “*.sh” in the directory.

Logical AND and OR

Double brackets let you use && and || as the logical AND and OR operators.

This script should resolve the conditional statement to true because 10 does equal 10 and 25 is less than 26.

Save this text into a file called “and.sh”, make it executable, and run it with:

The script executes as we’d expect.

This time we’ll use the || operator. The conditional statement should resolve to true because although 10 is not greater than 15, 25 is still less than 26. As long as either the first comparison or the second comparison is true, the conditional statement as a whole resolves to true.

Save this text as “or.sh” and make it executable.

Regexes

Double bracket conditional statements permit the use of the =~ operator, which applies the regex search patterns in a string to the other half of the statement. If the regex is satisfied the conditional statement is considered to be true. If the regex finds no matches the conditional statement resolves to false.

RELATED: How to Use Regular Expressions (regexes) on Linux

Save this text to a file called “regex.sh”, and make it executable.

The first set of double brackets uses the string variable $mask1 as the regex. This contains the pattern for all digits in the range of zero to nine. It applies this regex to the $words string variable.

The second set of double brackets again uses the string variable $mask1 as the regex, but this time it uses it with the $WordsandNumbers string variable.

The last set of double brackets uses a more complex regex mask in string variable $mask2 .

[A-Za-z0-9.
_%+-]+: This matches any character that is an uppercase or lowercase letter, or any digit from zero to nine, or a period, underscore, percentage sign, or plus or minus sign.
The “+” outside of the “[]” means repeat those matches for as many characters as it finds.
@: This matches the “@” character only.
[A-Za-z0-9.
-]+: This matches any character that is an uppercase or lowercase letter, or any digit from zero to nine, or a period or hyphen.
The “+” outside of the “[ ]” means repeat those matches for as many characters as it finds.
.
This matches the “. ” character only. [A-Za-z]{2,4}: This matches any uppercase or lowercase letter. The “{2,4}” means match at least two characters, and at most four.

Putting that all together, the regex mask will check whether an email address is correctly formed.

Save the script text into a file called “regex.sh” and make it executable. When we run the script we get this output.

The first conditional statement fails because the regex is looking for digits but there are no digits in the value held in the $words string variable.

The second conditional statement succeeds because the $WordsandNumbers string variable does contain digits.

The final conditional statement succeeds—that is, it resolves to true—because the email address is properly formatted.

Just One Condition

Double bracket conditional tests bring flexibility and legibility to your scripts. Just being able to use regexes in your conditional tests justifies learning how to use [[ and ]].

Just make sure the script calls a shell that supports them, like Bash.

RELATED: 15 Special Characters You Need to Know for Bash