|

Linux CLI 61 ๐Ÿง modern test version and in (( )) shell scripts

โฑ๏ธ Estimated read: 16 min | |

#!/bin/bash

var1="test"
var2="test case"

if [[ $var1 = test ]]; then
    echo "Using [[ ]], var1 is 'test'"
fi

if [[ "$var1" == "test" ]]; then
    echo "Using [[ ]], var1 is 'test' with quotes"
fi

if [[ $var2 =~ test.* ]]; then
    echo "Using [[ ]], var2 matches regex 'test.*'"
fi

if [[ 10 -eq 10 ]]; then
    echo "Using [[ ]], 10 is equal to 10"
fi

a=5
b=3
sum=$((a + b))
difference=$((a - b))
product=$((a * b))
quotient=$((a / b))
remainder=$((a % b))

echo "Sum: $sum"
echo "Difference: $difference"
echo "Product: $product"
echo "Quotient: $quotient"
echo "Remainder: $remainder"

num1=20
num2=5
result=$((num1 + num2 * 3))
echo "Result: $result"

counter=0
counter=$((counter + 1))
echo "Counter after increment: $counter"
counter=$((counter - 1))
echo "Counter after decrement: $counter"

base=2
exponent=3
result=$((base ** exponent))
echo "$base raised to the power of $exponent is $result"

The older [ ] test has been around since the earliest days of Unix. [[ ]] is bash’s modern replacement โ€” it fixes the quoting headaches, adds regex and pattern matching, and generally makes conditions safer to write. And (( )) is its arithmetic counterpart โ€” a cleaner way to do integer math than expr or let.

Key point: [[ ]] is bash-specific (also ksh and zsh). It’s not POSIX โ€” so if your script must run under dash or plain sh, stick with [ ]. But for modern bash scripts, [[ ]] is almost always the better choice.


a – extended test command in shell scripts

Unlike the traditional [ ] (or test), which requires spaces around operators, [[ ]] does not have this requirement. It’s generally recommended to use [[ ]] over [ ] for most modern shell scripting needs due to its enhanced functionality and error prevention.

The key differences:

Feature[ ][[ ]]
POSIXโœ…โŒ (bash/ksh/zsh)
Word splittingYesNo
Pathname expansionYesNo
Quote variablesRequiredOptional
&& / || insideNoYes
Regex =~NoYes
Pattern matching ==NoYes
< / >Escape neededBare works
Parentheses groupingNoYes

String comparison without quotes:

Without quotes, [ $var1 = test ] will fail if var1 is empty or contains spaces:

$ var1=""
$ [ $var1 = test ]
bash: [: =: unary operator expected

But [[ $var1 = test ]] handles it cleanly:

$ var1=""
$ [[ $var1 = test ]]
$ echo $?
[ 1 ]

The reason: [[ ]] doesn’t perform word splitting, so an empty variable is treated as an empty string โ€” not as “nothing at all.”

$ var1="test"
$ if [[ $var1 = test ]]; then echo "match"; fi
[ match ]

$ if [[ "$var1" == "test" ]]; then echo "match quoted"; fi
[ match quoted ]

Both work. The quotes are optional in [[ ]] โ€” but harmless and sometimes clearer.

Pattern matching:

[[ ]] supports glob patterns with ==:

$ file="report.txt"
$ if [[ $file == *.txt ]]; then echo "text file"; fi
[ text file ]

$ name="Alice"
$ if [[ $name == A* ]]; then echo "starts with A"; fi
[ starts with A ]

$ if [[ $name == *ice ]]; then echo "ends with ice"; fi
[ ends with ice ]

With [ ], this would expand the glob against the filesystem โ€” not what you want.

# โŒ In [ ], * is expanded by the shell before the test runs
[ "$file" = *.txt ]       # fragile

# โœ… In [[ ]], * is a pattern
[[ $file == *.txt ]]      # reliable

Regex matching with =~:

$ var2="test case"
$ if [[ $var2 =~ test.* ]]; then echo "matches regex"; fi
[ matches regex ]

$ email="user@example.com"
$ if [[ $email =~ ^[a-z]+@[a-z]+\.[a-z]{2,}$ ]]; then echo "valid"; fi
[ valid ]

$ phone="555-1234"
$ if [[ $phone =~ ^[0-9]{3}-[0-9]{4}$ ]]; then echo "valid phone"; fi
[ valid phone ]

The regex is not quoted โ€” quoting it would make it a literal string. To match a literal, put the pattern in a variable:

$ pattern="test.*"
$ if [[ $var2 =~ $pattern ]]; then echo "match"; fi
[ match ]

Numeric comparisons:

Both [ ] and [[ ]] support numeric comparisons the same way:

$ if [[ 10 -eq 10 ]]; then echo "equal"; fi
[ equal ]

$ a=5
$ b=10
$ if [[ $a -lt $b ]]; then echo "less"; fi
[ less ]

Combining conditions inside [[ ]]:

This is one of the biggest wins โ€” && and || work inside the brackets:

$ a=5
$ b=10
$ if [[ $a -gt 0 && $b -gt 0 ]]; then echo "both positive"; fi
[ both positive ]

$ if [[ $a -gt 100 || $b -gt 5 ]]; then echo "at least one"; fi
[ at least one ]

$ if [[ ! -f /nonexistent ]]; then echo "not a file"; fi
[ not a file ]

With [ ], you need separate brackets:

if [ $a -gt 0 ] && [ $b -gt 0 ]; then ...; fi

Grouping with parentheses:

$ if [[ ($a -gt 0 && $b -gt 0) || $a -eq 100 ]]; then
>     echo "condition met"
> fi
[ condition met ]

< and > without escaping:

In [ ], < and > are redirection operators โ€” you have to escape them:

# โŒ Creates a file called "b"
[ "$a" > "$b" ]

# โœ… Escaped
[ "$a" \> "$b" ]

In [[ ]], they work bare:

$ a="apple"
$ b="banana"
$ if [[ $a < $b ]]; then echo "a comes first"; fi
[ a comes first ]

File tests work the same:

$ if [[ -f /etc/passwd ]]; then echo "file"; fi
[ file ]

$ if [[ -d /etc ]]; then echo "dir"; fi
[ dir ]

$ if [[ -x /usr/bin/ls ]]; then echo "exec"; fi
[ exec ]

When to use [[ ]] vs [ ]:

Use [[ ]] whenUse [ ] when
Writing bash scriptsNeed POSIX portability
You want regex matchingRunning under sh or dash
You want glob patternsWriting portable scripts
You want && / `
You don’t want to quote
You want safer conditions

Rule of thumb: Default to [[ ]] in modern bash. Only fall back to [ ] if you need portability.


b – (( )) in shell scripts

(( )) is used for arithmetic evaluation. It allows you to perform mathematical operations and handle integer arithmetic directly within your scripts.

Syntax:

result=$((expression))

expression can include any valid arithmetic operation involving integers:

OperatorMeaning
+Addition
-Subtraction
*Multiplication
/Division
%Modulus (remainder)
**Exponentiation
++Increment
--Decrement
+=, -=, *=, /=Compound assignment

Key points:

  1. Integer arithmetic only โ€” (( )) supports only integer math. For floating point, use bc or awk.
  2. Expression evaluation โ€” the entire expression is evaluated to a single integer value.
  3. Precedence and associativity โ€” standard math rules apply: multiplication and division before addition and subtraction. Parentheses can change the order.

Example 1 โ€” Simple arithmetic operations:

a=5
b=3
sum=$((a + b))
difference=$((a - b))
product=$((a * b))
quotient=$((a / b))
remainder=$((a % b))

echo "Sum: $sum"
echo "Difference: $difference"
echo "Product: $product"
echo "Quotient: $quotient"
echo "Remainder: $remainder"
[ Sum: 8 ]
[ Difference: 2 ]
[ Product: 15 ]
[ Quotient: 1 ]
[ Remainder: 2 ]

Note: 5 / 3 is 1 โ€” integer division truncates. 5 % 3 is 2 โ€” the remainder.

Example 2 โ€” Using variables and expressions:

num1=20
num2=5
result=$((num1 + num2 * 3))
echo "Result: $result"
[ Result: 35 ]

The multiplication happens first (5 * 3 = 15), then the addition (20 + 15 = 35). To change the order, use parentheses:

$ result=$(( (num1 + num2) * 3 ))
$ echo "Result: $result"
[ Result: 75 ]

Example 3 โ€” Incrementing and decrementing variables:

counter=0
counter=$((counter + 1))
echo "Counter after increment: $counter"
[ Counter after increment: 1 ]

counter=$((counter - 1))
echo "Counter after decrement: $counter"
[ Counter after decrement: 0 ]

The shorter form:

$ counter=0
$ ((counter++))
$ echo $counter
[ 1 ]
$ ((counter--))
$ echo $counter
[ 0 ]

Or with compound assignment:

$ counter=10
$ ((counter += 5))
$ echo $counter
[ 15 ]
$ ((counter -= 3))
$ echo $counter
[ 12 ]
$ ((counter *= 2))
$ echo $counter
[ 24 ]
$ ((counter /= 4))
$ echo $counter
[ 6 ]

Example 4 โ€” Using exponentiation:

base=2
exponent=3
result=$((base ** exponent))
echo "$base raised to the power of $exponent is $result"
[ 2 raised to the power of 3 is 8 ]

Comparing (( )) forms:

There are three ways to use arithmetic in bash:

FormPurposeExample
$((...))Produce a valuex=$((a + b))
((...))Evaluate, set exit status((a > b))
letOlder alternativelet x=a+b

$((...)) vs ((...)):

  • $((...)) produces output โ€” you capture it or print it
  • ((...)) sets an exit status โ€” useful in if
# $(( )) produces a value
$ x=$((5 + 3))
$ echo $x
[ 8 ]

# (( )) sets an exit status
$ ((5 > 3))
$ echo $?
[ 0 ]
$ ((5 < 3))
$ echo $?
[ 1 ]

Using (( )) in conditions:

$ a=5
$ b=10
$ if ((a < b)); then echo "a is less"; fi
[ a is less ]

$ if ((a == 5 && b == 10)); then echo "both match"; fi
[ both match ]

$ if ((a > 0)); then echo "positive"; fi
[ positive ]

This is cleaner than [[ $a -lt $b ]] โ€” you write the comparison the way you’d write it in C.

(( )) vs [[ ]] for arithmetic:

# With [[ ]] โ€” bash test operators
if [[ $a -lt $b ]]; then ...; fi

# With (( )) โ€” C-style operators
if ((a < b)); then ...; fi

Both work. (( )) is more natural for math-heavy conditions.

Floating-point limitation:

(( )) only handles integers. For decimals, use bc or awk:

# โŒ Wrong โ€” bash arithmetic is integer only
$ result=$((10 / 3))
$ echo $result
[ 3 ]

# โœ… With bc
$ result=$(echo "scale=2; 10 / 3" | bc)
$ echo $result
[ 3.33 ]

# โœ… With awk
$ result=$(awk 'BEGIN {print 10 / 3}')
$ echo $result
[ 3.33333 ]

Common uses of (( )):

# Counters in loops
$ for ((i=1; i<=5; i++)); do echo "$i"; done
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]

# Conditional arithmetic
$ ((count++))
$ ((total += amount))

# Generate a random number
$ echo $((RANDOM % 100))
[ 42 ]

# Index an array
$ arr=(a b c d)
$ i=2
$ echo "${arr[$i]}"
[ c ]

C-style for loop:

#!/bin/bash
for ((i=1; i<=5; i++)); do
    echo "Iteration $i"
done
[ Iteration 1 ]
[ Iteration 2 ]
[ Iteration 3 ]
[ Iteration 4 ]
[ Iteration 5 ]

Arithmetic assignment operators:

$ x=10
$ ((x += 5));   echo $x    # 15
$ ((x -= 3));   echo $x    # 12
$ ((x *= 2));   echo $x    # 24
$ ((x /= 4));   echo $x    # 6
$ ((x %= 4));   echo $x    # 2
$ ((x **= 3));  echo $x    # 8

Bitwise operators (bonus):

OperatorMeaning
&AND
|OR
^XOR
~NOT
<<Left shift
>>Right shift
$ echo $((5 & 3))
[ 1 ]
$ echo $((5 | 3))
[ 7 ]
$ echo $((5 ^ 3))
[ 6 ]
$ echo $((1 << 4))
[ 16 ]
$ echo $((16 >> 2))
[ 4 ]

Comparison: $(( )) vs expr vs let:

MethodExampleNotes
$(( ))x=$((a+b))Modern, preferred
(( ))((a++))For side effects
letlet x=a+bOlder, still works
exprx=$(expr $a + $b)Obsolete, avoid
bcecho "a+b" | bcFor floating point

Rule of thumb: Use $(( )) for producing values, (( )) for conditions and side effects. Never use expr in new scripts.


Complete Example Session

# ============================================
# PART 1: [[ ]] WITH STRING COMPARISON
# ============================================

$ var1="test"
$ var2="test case"

$ if [[ $var1 = test ]]; then echo "match"; fi
[ match ]

$ if [[ "$var1" == "test" ]]; then echo "match quoted"; fi
[ match quoted ]

$ if [[ $var1 != other ]]; then echo "not other"; fi
[ not other ]

# ============================================
# PART 2: [[ ]] WITH REGEX
# ============================================

$ if [[ $var2 =~ test.* ]]; then echo "matches regex"; fi
[ matches regex ]

$ email="user@example.com"
$ if [[ $email =~ ^[a-z]+@[a-z]+\.[a-z]{2,}$ ]]; then echo "valid"; fi
[ valid ]

# ============================================
# PART 3: [[ ]] WITH GLOB PATTERNS
# ============================================

$ file="report.txt"
$ if [[ $file == *.txt ]]; then echo "text"; fi
[ text ]

$ name="Alice"
$ if [[ $name == A* ]]; then echo "starts with A"; fi
[ starts with A ]

# ============================================
# PART 4: [[ ]] WITH NUMBERS
# ============================================

$ if [[ 10 -eq 10 ]]; then echo "equal"; fi
[ equal ]

$ a=5
$ b=10
$ if [[ $a -lt $b ]]; then echo "less"; fi
[ less ]

# ============================================
# PART 5: [[ ]] WITH COMBINED CONDITIONS
# ============================================

$ a=5
$ b=10
$ if [[ $a -gt 0 && $b -gt 0 ]]; then echo "both positive"; fi
[ both positive ]

$ if [[ $a -gt 100 || $b -gt 5 ]]; then echo "at least one"; fi
[ at least one ]

$ if [[ ! -f /nonexistent ]]; then echo "not a file"; fi
[ not a file ]

# ============================================
# PART 6: [[ ]] WITH GROUPING
# ============================================

$ if [[ ($a -gt 0 && $b -gt 0) || $a -eq 100 ]]; then
>     echo "condition met"
> fi
[ condition met ]

# ============================================
# PART 7: [[ ]] WITH < AND >
# ============================================

$ a="apple"
$ b="banana"
$ if [[ $a < $b ]]; then echo "a first"; fi
[ a first ]

# ============================================
# PART 8: SIMPLE ARITHMETIC WITH $(( ))
# ============================================

$ a=5
$ b=3
$ echo $((a + b))
[ 8 ]
$ echo $((a - b))
[ 2 ]
$ echo $((a * b))
[ 15 ]
$ echo $((a / b))
[ 1 ]
$ echo $((a % b))
[ 2 ]

# ============================================
# PART 9: PRECEDENCE
# ============================================

$ num1=20
$ num2=5
$ echo $((num1 + num2 * 3))
[ 35 ]
$ echo $(((num1 + num2) * 3))
[ 75 ]

# ============================================
# PART 10: INCREMENT / DECREMENT
# ============================================

$ counter=0
$ ((counter++))
$ echo $counter
[ 1 ]
$ ((counter--))
$ echo $counter
[ 0 ]

# ============================================
# PART 11: COMPOUND ASSIGNMENT
# ============================================

$ x=10
$ ((x += 5));  echo $x
[ 15 ]
$ ((x -= 3));  echo $x
[ 12 ]
$ ((x *= 2));  echo $x
[ 24 ]
$ ((x /= 4));  echo $x
[ 6 ]

# ============================================
# PART 12: EXPONENTIATION
# ============================================

$ base=2
$ exponent=3
$ echo $((base ** exponent))
[ 8 ]

# ============================================
# PART 13: ARITHMETIC IN CONDITIONS
# ============================================

$ a=5
$ b=10
$ if ((a < b)); then echo "less"; fi
[ less ]

$ if ((a == 5 && b == 10)); then echo "both"; fi
[ both ]

# ============================================
# PART 14: C-STYLE FOR LOOP
# ============================================

$ for ((i=1; i<=5; i++)); do echo "$i"; done
[ 1 ]
[ 2 ]
[ 3 ]
[ 4 ]
[ 5 ]

# ============================================
# PART 15: RANDOM NUMBER
# ============================================

$ echo $((RANDOM % 100))
[ 42 ]

# ============================================
# PART 16: INTEGER DIVISION TRUNCATION
# ============================================

$ echo $((10 / 3))
[ 3 ]

# Float requires bc
$ echo "scale=2; 10 / 3" | bc
[ 3.33 ]

# Or awk
$ awk 'BEGIN {print 10 / 3}'
[ 3.33333 ]

# ============================================
# PART 17: BITWISE OPERATORS
# ============================================

$ echo $((5 & 3))
[ 1 ]
$ echo $((5 | 3))
[ 7 ]
$ echo $((5 ^ 3))
[ 6 ]
$ echo $((1 << 4))
[ 16 ]

# ============================================
# PART 18: FULL SCRIPT
# ============================================

$ cat > modern.sh << 'EOF'
#!/bin/bash

var1="test"
var2="test case"

if [[ $var1 = test ]]; then
    echo "Using [[ ]], var1 is 'test'"
fi

if [[ "$var1" == "test" ]]; then
    echo "Using [[ ]], var1 is 'test' with quotes"
fi

if [[ $var2 =~ test.* ]]; then
    echo "Using [[ ]], var2 matches regex 'test.*'"
fi

if [[ 10 -eq 10 ]]; then
    echo "Using [[ ]], 10 is equal to 10"
fi

a=5
b=3
sum=$((a + b))
difference=$((a - b))
product=$((a * b))
quotient=$((a / b))
remainder=$((a % b))

echo "Sum: $sum"
echo "Difference: $difference"
echo "Product: $product"
echo "Quotient: $quotient"
echo "Remainder: $remainder"

num1=20
num2=5
result=$((num1 + num2 * 3))
echo "Result: $result"

counter=0
counter=$((counter + 1))
echo "Counter after increment: $counter"
counter=$((counter - 1))
echo "Counter after decrement: $counter"

base=2
exponent=3
result=$((base ** exponent))
echo "$base raised to the power of $exponent is $result"
EOF

$ chmod +x modern.sh
$ ./modern.sh
[ Using [[ ]], var1 is 'test' ]
[ Using [[ ]], var1 is 'test' with quotes ]
[ Using [[ ]], var2 matches regex 'test.*' ]
[ Using [[ ]], 10 is equal to 10 ]
[ Sum: 8 ]
[ Difference: 2 ]
[ Product: 15 ]
[ Quotient: 1 ]
[ Remainder: 2 ]
[ Result: 35 ]
[ Counter after increment: 1 ]
[ Counter after decrement: 0 ]
[ 2 raised to the power of 3 is 8 ]

Quick Reference

[ ] vs [[ ]]

Feature[ ][[ ]]
POSIXโœ…โŒ
Quote requiredYesNo
Word splittingYesNo
Glob expansionYesNo
&& insideโŒโœ…
|| insideโŒโœ…
Regex =~โŒโœ…
Glob ==โŒโœ…
< / >EscapeBare
Grouping ()โŒโœ…

[[ ]] Operators

OperatorMeaning
= / ==String equality
!=String inequality
=~Regex match
-eqโ€“-geNumeric comparisons
-fโ€“-xFile tests
&&AND
||OR
!NOT
( )Grouping

(( )) Arithmetic

OperatorMeaning
+Addition
-Subtraction
*Multiplication
/Integer division
%Modulus
**Exponentiation
++ / --Increment / decrement
+= -= *= /=Compound assignment
& | ^ ~ << >>Bitwise

Three Forms of Arithmetic

FormPurposeExample
$(( ))Produce a valuex=$((a+b))
(( ))Evaluate, set status((a > b))
letOlder alternativelet x=a+b

(( )) in Conditions

ExpressionMeaning
((a < b))a less than b
((a <= b))a less or equal
((a == b))a equal to b
((a != b))a not equal
((a && b))both non-zero
((a || b))either non-zero
((!a))a is zero

Floating Point

ToolExample
bcecho "scale=2; 10/3" | bc
awkawk 'BEGIN {print 10/3}'

Best Practices

โœ… Do This:

# Use [[ ]] in bash scripts
if [[ $var == "value" ]]; then ...; fi       # โœ…

# Use regex for pattern matching
if [[ $email =~ ^[a-z]+@ ]]; then ...; fi    # โœ…

# Use glob patterns with ==
if [[ $file == *.txt ]]; then ...; fi        # โœ…

# Combine conditions inside [[ ]]
if [[ $a -gt 0 && $b -gt 0 ]]; then ...; fi  # โœ…

# Use (( )) for arithmetic conditions
if ((a < b)); then ...; fi                   # โœ…

# Use $(( )) for values
sum=$((a + b))                               # โœ…

# Use C-style for loops
for ((i=0; i<10; i++)); do ...; done         # โœ…

# Use bc or awk for floats
result=$(echo "scale=2; 10/3" | bc)          # โœ…

# Use compound assignment for clarity
((count += 1))                               # โœ…

โŒ Don’t Do This:

# Don't use [[ ]] in sh scripts
#!/bin/sh
[[ $a == b ]]                                # โŒ not POSIX

# Don't quote the regex in [[ =~ ]]
if [[ $s =~ "test.*" ]]; then ...; fi        # โŒ literal match
if [[ $s =~ test.* ]]; then ...; fi          # โœ…

# Don't use -eq for strings
[[ "$name" -eq "Alice" ]]                    # โŒ error
[[ "$name" == "Alice" ]]                     # โœ…

# Don't use = for numbers in [[ ]]
[[ $a = 5 ]]                                 # โš ๏ธ  string compare
((a == 5))                                   # โœ…

# Don't expect floats from (( ))
echo $((10 / 3))                             # โŒ gives 3
echo "scale=2; 10/3" | bc                    # โœ…

# Don't use expr in modern scripts
x=$(expr $a + $b)                            # โŒ obsolete
x=$((a + b))                                 # โœ…

# Don't forget spaces in (( )) when needed
((a=5))                                      # โš ๏ธ  assignment
((a == 5))                                   # โœ… comparison

Common Pitfalls

PitfallProblemSolution
[[ ]] in shNot POSIXUse [ ] or bash shebang
Quoted regexLiteral matchDon’t quote the pattern
Floats in (( ))TruncatedUse bc or awk
= vs == in [[ ]]Both workFine either way
((a=5))Assignment, not compareUse ((a == 5))
Missing $ in $(( ))Variable names OK$((a + b)) works
(( )) with stringsErrorUse [[ ]] for strings

Real-World Examples

1. String Comparison with [[ ]]

#!/bin/bash
var1="test"
if [[ $var1 = test ]]; then
    echo "var1 is 'test'"
fi
[ var1 is 'test' ]

2. Quoted Comparison

#!/bin/bash
var1="test"
if [[ "$var1" == "test" ]]; then
    echo "var1 is 'test' with quotes"
fi
[ var1 is 'test' with quotes ]

3. Regex Match

#!/bin/bash
var2="test case"
if [[ $var2 =~ test.* ]]; then
    echo "var2 matches regex 'test.*'"
fi
[ var2 matches regex 'test.*' ]

4. Numeric Comparison

#!/bin/bash
if [[ 10 -eq 10 ]]; then
    echo "10 is equal to 10"
fi
[ 10 is equal to 10 ]

5. Full Arithmetic

#!/bin/bash
a=5
b=3
echo "Sum: $((a + b))"
echo "Difference: $((a - b))"
echo "Product: $((a * b))"
echo "Quotient: $((a / b))"
echo "Remainder: $((a % b))"
[ Sum: 8 ]
[ Difference: 2 ]
[ Product: 15 ]
[ Quotient: 1 ]
[ Remainder: 2 ]

6. Operator Precedence

#!/bin/bash
num1=20
num2=5
result=$((num1 + num2 * 3))
echo "Result: $result"
[ Result: 35 ]

7. Increment / Decrement

#!/bin/bash
counter=0
counter=$((counter + 1))
echo "Counter: $counter"
counter=$((counter - 1))
echo "Counter: $counter"
[ Counter: 1 ]
[ Counter: 0 ]

8. Exponentiation

#!/bin/bash
base=2
exponent=3
result=$((base ** exponent))
echo "$base ^ $exponent = $result"
[ 2 ^ 3 = 8 ]

9. Email Validation

#!/bin/bash
read -p "Email: " email
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Valid"
else
    echo "Invalid"
fi

10. File Extension Check

#!/bin/bash
file="$1"
if [[ $file == *.txt ]]; then
    echo "Text file"
elif [[ $file == *.jpg || $file == *.png ]]; then
    echo "Image file"
else
    echo "Other"
fi

11. Numeric Range

#!/bin/bash
read -p "Score: " score
if ((score >= 90 && score <= 100)); then
    echo "A"
elif ((score >= 80)); then
    echo "B"
else
    echo "C or below"
fi

12. Countdown

#!/bin/bash
for ((i=10; i>0; i--)); do
    echo "$i"
    sleep 0.1
done
echo "Liftoff!"

13. Random Number

#!/bin/bash
echo "Random: $((RANDOM % 100))"
[ Random: 42 ]

14. Accumulator

#!/bin/bash
total=0
for n in 1 2 3 4 5; do
    ((total += n))
done
echo "Total: $total"
[ Total: 15 ]

15. Bitwise Operations

#!/bin/bash
echo "AND: $((5 & 3))"
echo "OR:  $((5 | 3))"
echo "XOR: $((5 ^ 3))"
echo "SHL: $((1 << 4))"
echo "SHR: $((16 >> 2))"
[ AND: 1 ]
[ OR:  7 ]
[ XOR: 6 ]
[ SHL: 16 ]
[ SHR: 4 ]

16. Float with bc

#!/bin/bash
a=10
b=3
result=$(echo "scale=2; $a / $b" | bc)
echo "$a / $b = $result"
[ 10 / 3 = 3.33 ]

17. Float with awk

#!/bin/bash
result=$(awk 'BEGIN {printf "%.2f", 10 / 3}')
echo "Result: $result"
[ Result: 3.33 ]

18. Power Table

#!/bin/bash
for i in 1 2 3 4 5; do
    echo "2^$i = $((2 ** i))"
done
[ 2^1 = 2 ]
[ 2^2 = 4 ]
[ 2^3 = 8 ]
[ 2^4 = 16 ]
[ 2^5 = 32 ]

19. Modern vs Old Test

#!/bin/bash
# Old way
if [ "$1" = "start" ] && [ -n "$1" ]; then
    echo "old style"
fi

# Modern way
if [[ $1 == "start" && -n $1 ]]; then
    echo "modern style"
fi

20. Full Modern Script

#!/bin/bash

# String tests with [[ ]]
name="Alice"
if [[ $name == A* ]]; then
    echo "Starts with A"
fi

if [[ $name =~ ^[A-Z] ]]; then
    echo "Starts with capital"
fi

# Numeric tests with (( ))
age=30
if ((age >= 18)); then
    echo "Adult"
fi

# Arithmetic
total=$((age * 2))
echo "Double age: $total"

# Counter loop
for ((i=1; i<=3; i++)); do
    echo "Iteration $i"
done
[ Starts with A ]
[ Starts with capital ]
[ Adult ]
[ Double age: 60 ]
[ Iteration 1 ]
[ Iteration 2 ]
[ Iteration 3 ]

Visual: [ ] vs [[ ]]

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           [ ] โ€” old test                     โ”‚
โ”‚                                              โ”‚
โ”‚  if [ "$var" = "value" ]; then               โ”‚
โ”‚      # must quote                            โ”‚
โ”‚  fi                                          โ”‚
โ”‚                                              โ”‚
โ”‚  if [ -f file ] && [ -r file ]; then         โ”‚
โ”‚      # separate brackets                     โ”‚
โ”‚  fi                                          โ”‚
โ”‚                                              โ”‚
โ”‚  โ€ข Word splitting                            โ”‚
โ”‚  โ€ข Glob expansion                            โ”‚
โ”‚  โ€ข No regex                                  โ”‚
โ”‚  โ€ข POSIX                                     โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           [[ ]] โ€” modern test                โ”‚
โ”‚                                              โ”‚
โ”‚  if [[ $var == "value" ]]; then              โ”‚
โ”‚      # no quotes needed                      โ”‚
โ”‚  fi                                          โ”‚
โ”‚                                              โ”‚
โ”‚  if [[ -f file && -r file ]]; then           โ”‚
โ”‚      # combine inside                        โ”‚
โ”‚  fi                                          โ”‚
โ”‚                                              โ”‚
โ”‚  if [[ $email =~ @.*\.com$ ]]; then          โ”‚
โ”‚      # regex!                                โ”‚
โ”‚  fi                                          โ”‚
โ”‚                                              โ”‚
โ”‚  โ€ข No word splitting                         โ”‚
โ”‚  โ€ข No glob expansion                         โ”‚
โ”‚  โ€ข Regex and globs                           โ”‚
โ”‚  โ€ข bash / ksh / zsh                          โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Visual: $(( )) vs (( ))

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           $(( )) โ€” produces a value          โ”‚
โ”‚                                              โ”‚
โ”‚  sum=$((a + b))                              โ”‚
โ”‚  echo "Sum: $sum"                            โ”‚
โ”‚                                              โ”‚
โ”‚  The result is captured or printed.          โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚           (( )) โ€” sets exit status           โ”‚
โ”‚                                              โ”‚
โ”‚  if ((a < b)); then                          โ”‚
โ”‚      echo "a is less"                        โ”‚
โ”‚  fi                                          โ”‚
โ”‚                                              โ”‚
โ”‚  ((counter++))                               โ”‚
โ”‚                                              โ”‚
โ”‚  Used for conditions and side effects.       โ”‚
โ”‚                                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ConceptSyntaxExample
Modern test[[ EXPR ]][[ $a == b ]]
String equality== / =[[ $name == Alice ]]
String inequality!=[[ $a != $b ]]
Regex match=~[[ $s =~ pat ]]
Glob pattern==[[ $f == *.txt ]]
Numeric compare-eqโ€“-ge[[ $a -lt $b ]]
File test-fโ€“-x[[ -f file ]]
AND&&[[ $a && $b ]]
OR||[[ $a || $b ]]
NOT![[ ! -f f ]]
Grouping( )[[ (a || b) && c ]]
Arithmetic value$(( ))x=$((a+b))
Arithmetic condition(( ))((a < b))
Increment++((i++))
Decrement--((i--))
Compound assign+= -= *= /=((x += 5))
Exponentiation**$((2 ** 3))
Bitwise& | ^ ~ << >>$((5 & 3))
C-style forfor ((...))for ((i=0;i<5;i++))
Float with bc| bcecho "10/3" | bc
Float with awkawkawk 'BEGIN{print 10/3}'

Key takeaways:

  • [[ ]] is the modern bash test โ€” safer, more powerful, and easier to write than [ ]
  • No word splitting or glob expansion inside [[ ]] โ€” variables don’t need quotes
  • =~ enables regex matching; == with a pattern enables glob matching
  • && and || work inside [[ ]] โ€” no need for multiple brackets
  • < and > work bare โ€” no escaping needed
  • [[ ]] is not POSIX โ€” for sh or dash, use [ ]
  • $(( )) produces a value โ€” capture it or print it
  • (( )) sets an exit status โ€” use it in if and for side effects
  • Arithmetic is integer-only โ€” use bc or awk for floats
  • Precedence is standard math โ€” use parentheses to change the order
  • ++, --, and compound assignment make counters clean
  • ** is exponentiation โ€” bash-specific but very handy
  • C-style for loops (for ((i=0;i<n;i++))) are clearer than seq for numeric ranges

Remember: In modern bash, reach for [[ ]] first. It’s safer and more capable than [ ], and it handles empty variables, globs, and regex gracefully. Use $(( )) when you want a value and (( )) when you want a condition. Know that arithmetic is integer-only, and reach for bc or awk when you need decimals. Master these two constructs, and your conditions and calculations become clean, correct, and idiomatic bash.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!