Difference between single and double square brackets in Bash
Single []
are posix shell compliant condition tests.
Double [[]]
are an extension to the standard []
and are supported by bash and other shells (e.g. zsh, ksh). They support extra operations (as well as the standard posix operations). For example: ||
instead of -o
and regex matching with =~
. A fuller list of differences can be found in the bash manual section on conditional constructs.
Use []
whenever you want your script to be portable across shells. Use [[]]
if you want conditional expressions not supported by []
and don't need to be portable.
Are double square brackets [[ ]] preferable over single square brackets [ ] in Bash?
[[
has fewer surprises and is generally safer to use. But it is not portable - POSIX doesn't specify what it does and only some shells support it (beside bash, I heard ksh supports it too). For example, you can do
[[ -e $b ]]
to test whether a file exists. But with [
, you have to quote $b
, because it splits the argument and expands things like "a*"
(where [[
takes it literally). That has also to do with how [
can be an external program and receives its argument just normally like every other program (although it can also be a builtin, but then it still has not this special handling).
[[
also has some other nice features, like regular expression matching with =~
along with operators like they are known in C-like languages. Here is a good page about it: What is the difference between test, [
and [[
? and Bash Tests
How can I compare numbers in Bash?
In Bash, you should do your check in an arithmetic context:
if (( a > b )); then
...
fi
For POSIX shells that don't support (())
, you can use -lt
and -gt
.
if [ "$a" -gt "$b" ]; then
...
fi
You can get a full list of comparison operators with help test
or man test
.
Why is [[ 10 2 ]] true when comparing numbers in bash?
Because you compare strings according to Lexicographical order and not numbers
You may use [[ 10 -lt 2 ]]
and [[ 20 -lt 2 ]]
. -lt
stands for Less than (<
). For Greater than (>
) -gt
notation can be used instead.
In bash double parenthesis can be used as well for performing numeric comparison:
if ((10 < 2)); then echo "yes"; else echo "no"; fi
The above example will echo no
Shell equality operators (=, ==, -eq)
=
and ==
are for string comparisons-eq
is for numeric comparisons-eq
is in the same family as -lt
, -le
, -gt
, -ge
, and -ne
==
is specific to bash (not present in sh (Bourne shell), ...). Using POSIX =
is preferred for compatibility. In bash the two are equivalent, and in sh =
is the only one that will work.
$ a=foo
$ [ "$a" = foo ]; echo "$?" # POSIX sh
0
$ [ "$a" == foo ]; echo "$?" # bash-specific
0
$ [ "$a" -eq foo ]; echo "$?" # wrong
-bash: [: foo: integer expression expected
2
(Note: make sure to quote the variable expansions. Do not leave out the double-quotes above.)
If you're writing a #!/bin/bash
script then I recommend using [[
instead. The double square-brackets [[...]]
form has more features, a more natural syntax, and fewer gotchas that will trip you up. For example, double quotes are no longer required around $a
:
$ [[ $a == foo ]]; echo "$?" # bash-specific
0
See also:
- What's the difference between [ and [[ in Bash?
Single quote inside of double quoted string on linux command line
According to the bash man page:
Enclosing characters in single quotes preserves the literal value
of each character within the quotes. A single quote may not occur
between single quotes, even when preceded by a backslash.
So the answer is, you can't include a single quote within a single-quoted string no matter how you try. But depending on how your script is set up you may be able to use '\''
, which will end the first single quote, escape the second, and start another single-quoted string with the third.
How to do a logical OR operation for integer comparison in shell scripting?
This should work:
#!/bin/bash
if [ "$#" -eq 0 ] || [ "$#" -gt 1 ] ; then
echo "hello"
fi
I'm not sure if this is different in other shells but if you wish to use <, >, you need to put them inside double parenthesis like so:
if (("$#" > 1))
...
Assumption with double quotes, single quotes & no quotes
It looks like you are looking for exceptions, and I'd guess you have some in mind. I'm going to make the assumption that set -f
/set -o noglob
are being excluded from this case?
When you use the dd
command, globbing will not occure, even if unquoted.
$ ls *.txt
blah.txt file1.txt file2.txt file.txt logfile.txt
$ dd if=*.txt of=glob.txt
dd: failed to open ‘*.txt’: No such file or directory
Rebuttal and false positives
Here are some examples that are odd, but follow expansion
variable='2010-09-08 12:34:56'
echo "$variable" | xargs date +%s -d
date: extra operand '12:34:56'
The extra operand shows that variable expansion is happening, you are losing the quotes in the pipe.$ date +%s -d 2010-09-08 12:34:56
date: extra operand ‘12:34:56’
This also happens if you create a script to echo $1
and then expand your quoted variable while passing. It expands, and works as expected. So, the issue is not with xargs, but with your expansion before the pipe which is normal.
- Eval... evals whole purpose is to do expansion of its args prior to running a command. Expansion also happens with
bash -c
, except it takes one argument. So, again, this is not an expansion issue, but a command usage issue.
cmd='printf "%s\n" "$(date -d "$variable" +%c)"'
bash -c $cmd
works the same as the expanded version
$ bash -c printf "%s\n" "$(date -d "$variable" +%c)"
printf: usage: printf [-v var] format [arguments]
I really enjoyed Hauri's $'...' and $"..." information--however, those are not the samething we are talking about. They are in fact behaving as the bash man page says they should. $'' is as different from '' as (()) is from $(())
I got excited about this one, so...
$ ls
mclark.txt qt sign_in.txt skel.bash
$ zip m*t.zip *t
$ ls *.zip
m*t.zip
However, this isn't right either-- the splat expands, but upon no match zip uses it as a literal. I found a few commands that did this, but if there was a match (I added a my.zip later) it uses the matched expansion (an error was thrown, b/c my.zip was a text file for testing purposes).
Related Topics
Is There Any Way for Ioctl() in Linux to Specify Submission Queue Id for a Nvme Io Request
Rename Multiple Files - Linux/Ubuntu
Cygwin Xwin Server Randomly Loses Connection
Text Encoding Between Linux and Windows
Shell Script Linux Substract Parameter Grep
Mq Explorer - Could Not Load Swt Library
Cannot Kill Redis-Server on Linux
Sublime Text 2 Build (Ctrl +B) Intel Fortran Compiler
Reading Microphone Data by Polling Using Alsa [Or V4L2]
Arm Linux ":Start_Kernel Is Not Calling After Decompressing UImage"
Putting Extensions in a Certificate in Openssl
Linux Tcp Connect with Select() Fails at Testserver