Sagar.BlogArticle
All posts
All posts
Bash

String Operations in Bash

Length, slicing, search-and-replace, case conversion, and trimming — Bash has a surprisingly powerful set of built-in string operations that don't need external tools.

January 7, 20267 min read
BashStringsScripting

Bash's ${parameter%…} and related expansions let you slice, strip, and transform strings without spawning subshells or calling sed/awk. These built-in operations are fast, readable, and available in every Bash 3+ environment.

String Length

str="Hello, World!"
echo "${#str}"       # 13 — counts all characters including space and !

Substrings — Slicing

str="Hello, World!"
#     0123456789...

echo "${str:0:5}"    # Hello       — from index 0, length 5
echo "${str:7}"      # World!      — from index 7 to end
echo "${str:7:5}"    # World       — from index 7, length 5
echo "${str: -6}"    # orld!  — last 6 chars (space before - is required)
echo "${str: -6:5}"  # orld       — last 6 chars, take 5

Strip Prefix and Suffix

These are invaluable for extracting filenames, extensions, and path components without calling basename or dirname.

path="/home/alice/docs/report.tar.gz"

# Strip shortest prefix matching pattern
echo "${path#*/}"       # home/alice/docs/report.tar.gz  (removes leading /)

# Strip longest prefix matching pattern
echo "${path##*/}"      # report.tar.gz  (removes everything up to last /)
# Equivalent to: basename "$path"

# Strip shortest suffix matching pattern
echo "${path%.*}"       # /home/alice/docs/report.tar    (removes .gz)

# Strip longest suffix matching pattern
echo "${path%%.*}"      # /home/alice/docs/report        (removes .tar.gz)

# Practical: get filename without any extension
filename="${path##*/}"    # report.tar.gz
noext="${filename%%.*}"   # report

# vs ## and % vs %%

# (one hash) → strip shortest match from the start ## (two hashes) → strip longest match from the start % (one percent) → strip shortest match from the end %% (two percents) → strip longest match from the end

Memory aid: # comments are at the start of lines; % is at the end like end-of-line percent.

Search and Replace

text="the cat sat on the mat"

# Replace first match
echo "${text/cat/dog}"         # the dog sat on the mat

# Replace all matches
echo "${text//the/a}"          # a cat sat on a mat

# Replace only at start (prefix)
echo "${text/#the/A}"          # A cat sat on the mat

# Replace only at end (suffix)
echo "${text/%mat/rug}"        # the cat sat on the rug

# Delete a pattern (replace with nothing)
echo "${text/the /}"           # cat sat on the mat (first occurrence)
echo "${text//the /}"          # cat sat on mat (all occurrences)

Case Conversion (Bash 4+)

str="Hello World"

echo "${str,,}"      # hello world  — all lowercase
echo "${str^^}"      # HELLO WORLD  — all uppercase
echo "${str,}"       # hELLO WORLD  — lowercase first char only
echo "${str^}"       # Already uppercase — uppercase first char (no-op if already upper)

# Practical: normalise user input
read -r -p "Enter choice (yes/no): " answer
answer="${answer,,}"   # convert to lowercase before checking
if [[ "$answer" == "yes" ]]; then echo "Got yes"; fi
Quick Check

Given `file="/var/log/syslog"`, what does `${file##*/}` return?

Exercise

Given a variable filepath="/home/user/projects/my-app.tar.gz", use parameter expansion (no calling basename or dirname) to extract:

  1. Just the filename: my-app.tar.gz
  2. The directory: /home/user/projects
  3. The filename without all extensions: my-app
  4. Just the first extension: tar