bash - What do the brackets in if-statements do?
Solution 1
On any Unix other than those that have case-insensitive filesystems (like macOS), the if
statement
if ! $flag; then
echo "No brackets: Flag is $flag"
fi
would generate the output
bash: False: command not found
No brackets: Flag is False
On macOS, the error would not be generate as the False
command is the same as the standard (external) false
command due to the case-insensitivity of the filesystem.
In either case, the test on ! $flag
is true (but for different reasons), so the text is printed. On most Unices, ! $flag
is true since it runs the False
command, fails, and the failure is negated. On macOS, it runs the external false
utility, which returns false, which is negated.
The second if
statement,
if [ ! $flag ]; then
echo "With brackets: Flag is $flag"
fi
works the same way on any Unix. It calls the [
utility, which is the same as the test
utility (but [
requires a matching ]
as its last operand), with the two arguments !
and False
. By default, test
will return a true value if the string argument has non-zero length, which it has, but it's negated by the !
so test
returns false and the echo
is never executed.
For clarity, it is better to use the -n
test to test for a non-empty string:
if [ -n "$flag" ]; then ...
or -z
to test for an empty string:
if [ -z "$flag" ]; then ...
The quoting of the variable expansions is not important in this particular piece of code as $flag
expands to a single word that is not a filename globbing pattern. In general though, variable expansions should be double quoted to avoid word splitting and filename globbing.
For a discussion about [
vs. [[
, see
Related:
-
man test
on your system, andhelp test
in anybash
shell. - Why does my shell script choke on whitespace or other special characters?
Solution 2
The exact answer is a bit complex. It boils down to understanding that the word false
is both a command and an string (depending on context).
A longer description:
Lets start with the string A_Non_Existing_Command.
str=A_Non_Existing_Command
So, a (In general: please quote variable expansions.)
$ if $str; then echo yes; else echo no; fi
bash: A_Non_Existing_Command: command not found
no
Will print no, as the command executed fails (it doesn't exist) and the else part gets executed. Of course, there is an error message as well (more later).
However, the test command, either test
or [
(read help test
inside bash) will test if:
the length of the string between brackets is non-zero
And, so:
$ if [ "$str" ]; then echo yes; else echo no; fi
yes
Prints a yes (as the string has a non-zero amount of characters (usually bytes)). It doesn't matter that the string is also the name of a non-existing command. In this case the value of the variable is interpreted (evaluated) as an string.
And, of course, the negative test will print no:
$ if [ ! "$str" ]; then echo yes; else echo no; fi
no
Which is exactly equivalent to [ -z "$str" ]
.
Now, changing the str
content to False
and executing the same commands:
$ str=false
$ if "$str" ; then echo yes; else echo no; fi
no
$ if [ "$str" ]; then echo yes; else echo no; fi
yes
$ if [ ! "$str" ]; then echo yes; else echo no; fi
no
If you get the same with str=False
(without errors) is probably because you are in a system with a case-insensitive filesystem (where searching for False will find false without any error).
Related videos on Youtube
dkv
Updated on September 18, 2022Comments
-
dkv almost 2 years
Perhaps this applies to more than just bash, but I am confused about the role of brackets in if-statements. Most examples seem to have the following format
if [ expression ]; then #do stuff fi
But this doesn't always work for me. For example I have the following script
test.sh
:#!/bin/bash flag=False if ! $flag; then echo "No brackets: Flag is $flag" fi if [ ! $flag ]; then echo "With brackets: Flag is $flag" fi echo "The end."
Which prints
$ ./test.sh No brackets: Flag is False The end.
So the statement using brackets is either ignored or it evaluates to False. Why does this happen? What do the brackets do?
I've also seen double brackets. How are those used?
--
Edit: the questions claimed as duplicates (this and this) answer only a small part of this question. I'm still unclear why the syntax with brackets would fail (it seems to me that
test ! false
should evaluate totrue
) and why the syntax without brackets succeeds (although, as @ilkkachu mentions in the comment, it seems like it should actually fail as well?).-
LittleSmurfie about 7 years
-
JayJay about 7 yearsThis could be a good reference for you to read over. linuxacademy.com/blog/linux/…
-
ilkkachu about 7 years
flag=False; if ! $flag ; then ...
should give you an error, unless you really have a command calledFalse
. (false
would be standard, however) -
Michael D. about 7 years... for me
if [ !$flag ]; then
works (no space between!
and$flag
) -
Krzysztof Stasiak about 7 yearsI have googled that if test have 2 arguments and If the first argument is ! (exclamation mark), the expression is true if, and only if, the second argument is null. i found it here
-
Kusalananda over 5 years@ilkkachu They may be running macOS on a case-insensitive filesystem.
-
ilkkachu over 5 years@Kusalananda, yeah, thanks for reminding me of that horror, I was happy to have forgotten that. (The joke here is that it's a Mac I have on my lap right now, and
False
and "True" work just fine. rolleyes)
-
-
Kusalananda over 5 years@Christopher Hmm... My main disk is still case-insensitive with APFS, but that may be because it's been converted from case-insensitive HFS+ with the Mojave update. BTW, HFS+ had a case-sensitive option too, but the default was case-insensitive.
-
Stéphane Chazelas over 5 yearsWhether
$flag
expand to one or more words and which depends on the value of$IFS
. It still doesn't make sense to leave it unquoted when you know$flag
containsFalse
. -
Kusalananda over 5 years@StéphaneChazelas Bash resets
IFS
for new shells, and the variable is not set in the script. How could$flag
be split in multiple words here? -
Stéphane Chazelas over 5 yearsYes, it may be OK if that script in its entirety is not modified or that code is not copy pasted elsewhere (but now it's on a Q&A website, so it's too late) and that script is not sourced. Those are the conditions we'd need to state to say it may be acceptable not to quote. Why would you not quote though?