5.4. Arithmetic and String Operations#

Bash is fundamentally a string-processing language, but you often need to do math. This section covers arithmetic operations and string manipulation—essential for most scripts.

5.4.1. Quick Reference#

Task

Syntax

Example

Arithmetic

$((expr))

$((5 + 3))

Increment

$((++x)) or ((x++))

((count++))

Concatenate

Implicit

"$var1$var2"

String length

${#var}

${#filename}

Substring

${var:start:length}

${text:0:5}

Replace

${var/old/new}

${path//\//\\}

Case change

${var^^}

${name^^}

Remove prefix

${var#pattern}

${file#*/}

Remove suffix

${var%pattern}

${file%.*}

5.4.2. Common Mistakes#

5.4.2.1. ❌ Treating Variables as Numbers Automatically#

# WRONG: No arithmetic happens
$ x=5
$ y=3
$ echo $x + $y
5 + 3  # Just concatenation!

# RIGHT: Use arithmetic expansion
$ echo $((x + y))
8

5.4.2.2. ❌ Spaces in Parameter Substitution#

# Works
$ echo ${var:0:3}

# Doesn't work as expected
$ echo ${ var : 0 : 3 }
# This is syntax error or wrong expansion

5.4.2.3. ❌ String Comparison with Numbers#

# WRONG: String comparison, not numeric
$ if [ "10" -lt "9" ]; then
    echo "False (string comparison)"
fi

# RIGHT: Numeric comparison
$ if [ 10 -lt 9 ]; then
    echo "False (numeric comparison)"
fi

5.4.2.4. ❌ Modifying Variable While Expanding#

# This doesn't do what you might expect
$ x=5
$ x=$((x + 1))
$ echo $x
6

# The expansion happens in subshell sometimes
$ (x=5; echo $((x + 1)))
6
$ echo $x
5  # Original unchanged (subshell)

5.4.3. Practical Examples#

5.4.3.1. Count Characters#

#!/bin/bash

text="The quick brown fox"

# Total characters
total=${#text}

# Count a specific character
# (using parameter substitution)
search="o"
count=${text//$search}
count=$((${#text} - ${#count}))

echo "Total characters: $total"
echo "Count of '$search': $count"

5.4.3.2. Build a Path#

#!/bin/bash

# Dynamic path construction
user="alice"
project="myapp"
version="2.1"
base="/opt"

app_dir="${base}/${user}/${project}/${version}"
echo "App directory: $app_dir"

# Create it
mkdir -p "$app_dir"

5.4.3.3. Validate and Combine Arguments#

#!/bin/bash

# Get arguments with defaults
input="${1:-input.txt}"
output="${2:-output.txt}"

# Validate lengths
if [ ${#input} -eq 0 ] || [ ${#output} -eq 0 ]; then
    echo "Error: filenames required"
    exit 1
fi

# Process
grep "pattern" "$input" > "$output"

5.4.3.4. Extract Parts from Filename#

#!/bin/bash

full_path="/home/alice/documents/report.2025.pdf"

# Extract components
filename="${full_path##*/}"           # report.2025.pdf
directory="${full_path%/*}"           # /home/alice/documents
extension="${filename##*.}"           # pdf
name="${filename%.*}"                 # report.2025
base="${filename%%.*}"                # report

echo "File: $filename"
echo "Directory: $directory"
echo "Name: $name"
echo "Extension: $extension"
echo "Base: $base"

5.4.3.5. Calculate File Size#

#!/bin/bash

# Get file size in bytes
filesize=$(stat -f%z "myfile.txt")

# Convert to MB
size_mb=$((filesize / 1048576))

echo "File size: $size_mb MB"

# Check if over limit
max_size=100
if [ $size_mb -gt $max_size ]; then
    echo "File exceeds ${max_size}MB limit"
fi

5.4.3.6. Counter/Loop with Arithmetic#

#!/bin/bash

# Simple counter
count=0
for item in item1 item2 item3; do
    ((count++))
    echo "$count. $item"
done

# Countdown
count=5
while [ $count -gt 0 ]; do
    echo $count
    ((count--))
    sleep 1
done
echo "Blast off!"

5.4.4. Substring Operations#

Already covered in section 0504:

$ text="hello"

# Characters 0-2
$ echo ${text:0:3}
hel

# From position 1 to end
$ echo ${text:1}
ello

# Last 2 characters
$ echo ${text: -2}
lo

5.4.5. Trimming Whitespace#

Remove leading/trailing spaces:

# Remove all whitespace
$ text="  hello  world  "
$ echo ${text}
  hello  world
# Quotes preserve spaces

$ echo ${text// /}
helloworld
# Remove all spaces

# Trim leading whitespace
$ echo ${text##*( )}
hello  world
# Remove trailing whitespace  
$ echo ${text%%*( )}
  hello  world

More practical approach using xargs:

$ text="  hello  world  "
$ echo "$text" | xargs
hello world

5.4.6. Case Conversion (Bash 4+)#

Convert strings to different cases:

# To uppercase
$ name="alice"
$ echo ${name^^}
ALICE

# To lowercase
$ NAME="ALICE"
$ echo ${NAME,,}
alice

# Capitalize first letter
$ name="alice"
$ echo ${name^}
Alice

# Lowercase first letter
$ NAME="Alice"
$ echo ${NAME,}
alice

5.4.7. String Comparison#

Compare strings (Chapter 6 covers more):

# Check if strings are equal
$ if [ "$str1" = "$str2" ]; then
    echo "Same"
fi

# Check if strings are different
$ if [ "$str1" != "$str2" ]; then
    echo "Different"
fi

# Check if string is empty
$ if [ -z "$str" ]; then
    echo "Empty"
fi

# Check if string is not empty
$ if [ -n "$str" ]; then
    echo "Not empty"
fi

5.4.8. String Length#

Get length of a string (covered briefly before):

$ text="hello world"
$ echo ${#text}
11

# Validate length
$ password="secret"
if [ ${#password} -lt 8 ]; then
    echo "Password too short"
fi

5.4.9. String Concatenation#

Bash concatenation is implicit (no operator needed):

# Simply place strings next to each other
$ greeting="Hello"
$ name="Alice"
$ message="$greeting, $name!"
$ echo $message
Hello, Alice!

# Without spaces
$ file="data"
$ ext="txt"
$ filename="${file}.${ext}"
$ echo $filename
data.txt

# Concatenate with variable and literal
$ count=5
$ message="Found $count errors"
$ echo $message
Found 5 errors

5.4.10. The let Command#

An older (but still valid) way to do arithmetic:

# let is equivalent to $((...))
$ let sum=5+3
$ echo $sum
8

# Can use let for multiple operations
$ let x=5
$ let x=x+1
$ echo $x
6

# Shorthand for increment
$ let x++

But $((x + 1)) is clearer in modern bash.

5.4.11. Arithmetic: The Basics#

Remember: bash stores everything as strings. To do math, you need special syntax.

5.4.11.1. Arithmetic Expansion: $((…))#

The primary way to do math in bash:

# Basic arithmetic
$ x=5
$ y=3
$ echo $((x + y))
8

# Store result in variable
$ sum=$((x + y))
$ echo $sum
8

# Arithmetic without variables
$ echo $((10 + 5))
15

# More complex expressions
$ echo $((10 * 5 - 3))
47

# Division (integer only)
$ echo $((10 / 3))
3
# Result is integer, remainder discarded

# Modulo (remainder)
$ echo $((10 % 3))
1

5.4.11.2. Operators#

Operator

Meaning

Example

+

Addition

$((5 + 3)) = 8

-

Subtraction

$((5 - 3)) = 2

*

Multiplication

$((5 * 3)) = 15

/

Division (integer)

$((10 / 3)) = 3

%

Modulo (remainder)

$((10 % 3)) = 1

**

Exponentiation

$((2 ** 3)) = 8

++

Increment

$((++x)) or $((x++))

--

Decrement

$((--x)) or $((x--))

5.4.11.3. Variable Increment/Decrement#

# Pre-increment (increment, then use)
$ x=5
$ echo $((++x))
6
$ echo $x
6

# Post-increment (use, then increment)
$ x=5
$ echo $((x++))
5
$ echo $x
6

# Shortcuts
$ ((x++))      # Increment x
$ echo $x
6

$ ((y--))      # Decrement y
$ echo $y
(if y was 5, now it's 4)