Bash String Manipulation: Substring, Replace, Length, and More

When you write Bash scripts, many small text tasks do not need sed, awk, or another external command. Parameter expansion can extract part of a string, replace text, strip a prefix, or change case directly inside ${}.
This guide covers the most useful string operations with practical examples for each one.
String Length
Get the number of characters in a variable using ${#variable}:
filename="backup-2026-04-14.tar.gz"
echo "${#filename}"24This returns the character count. For strings that contain only ASCII characters, the character count and byte count are the same.
Extracting a Substring
Use ${variable:offset:length} to extract part of a string:
str="Hello, World"
echo "${str:7:5}"WorldThe offset is zero-based, so 7 starts at the eighth character. 5 is how many characters to return.
Omit the length to extract from the offset to the end of the string:
echo "${str:7}"WorldNegative offsets count from the end of the string. Note the space before the minus sign; it is required to avoid being interpreted as the :- default value syntax:
echo "${str: -5}"WorldReplacing a Substring
Use ${variable/pattern/replacement} to replace the first match of a pattern:
path="/var/log/app/error.log"
echo "${path/log/logs}"/var/logs/app/error.logOnly the first occurrence is replaced. To replace every occurrence, double the forward slash:
echo "${path//log/logs}"/var/logs/app/error.logsLeave the replacement empty to delete a substring:
version="v1.2.3"
echo "${version/v/}"1.2.3The pattern supports glob characters: * matches any sequence of characters, ? matches a single character, and [...] matches a character class.
Removing a Prefix
Use # to remove a prefix from the beginning of a string. The shortest matching prefix is removed:
file="photo.backup.tar.gz"
echo "${file#*.}"backup.tar.gzUse ## to remove the longest possible matching prefix:
echo "${file##*.}"gzA common use is stripping a directory path to get just the filename:
filepath="/home/user/documents/report.pdf"
echo "${filepath##*/}"report.pdfRemoving a Suffix
Use % to strip a suffix from the end of a string. The shortest match is removed:
file="photo.backup.tar.gz"
echo "${file%.*}"photo.backup.tarUse %% for the longest match:
echo "${file%%.*}"photoCombining prefix and suffix removal is a clean way to extract a bare filename from a path:
filepath="/home/user/documents/report.pdf"
base="${filepath##*/}"
name="${base%.*}"
echo "$name"reportChanging Case
Convert a string to uppercase with ^^:
greeting="hello, world"
echo "${greeting^^}"HELLO, WORLDConvert to lowercase with ,,:
input="Hello World"
echo "${input,,}"hello worldTo capitalize only the first character, use a single ^:
word="linux"
echo "${word^}"LinuxTo lowercase only the first character, use a single ,:
word="LINUX"
echo "${word,}"lINUXCase conversion requires Bash 4.0 or newer. The default shell on older macOS systems ships with Bash 3.2 and does not support these operators. As a portable alternative, use tr:
echo "$greeting" | tr '[:lower:]' '[:upper:]'Checking if a String Contains a Substring
Use a glob pattern inside [[ ]] to test whether a string contains another string:
str="Running on Linux kernel 6.8"
if [[ "$str" == *"Linux"* ]]; then
echo "Linux detected"
fiLinux detectedThe * wildcards match anything before and after the search pattern. The comparison is case-sensitive. To check case-insensitively, convert both sides to the same case first using ,, before comparing.
For a more detailed walkthrough of substring detection, including case-insensitive matching and edge cases, see How to Check if a String Contains a Substring in Bash
. For more on [[ ]] conditionals and string comparison operators, see the guide on Bash comparison operators
.
Quick Reference
For a printable quick reference, see the Bash cheatsheet .
| Operation | Syntax |
|---|---|
| String length | ${#var} |
| Substring from offset | ${var:offset} |
| Substring with length | ${var:offset:length} |
| Replace first match | ${var/pattern/replacement} |
| Replace all matches | ${var//pattern/replacement} |
| Delete substring | ${var/pattern/} |
| Remove shortest prefix | ${var#pattern} |
| Remove longest prefix | ${var##pattern} |
| Remove shortest suffix | ${var%pattern} |
| Remove longest suffix | ${var%%pattern} |
| Uppercase all | ${var^^} |
| Lowercase all | ${var,,} |
| Uppercase first char | ${var^} |
| Lowercase first char | ${var,} |
Conclusion
Bash parameter expansion handles most day-to-day string operations without extra commands, which keeps scripts shorter and faster. For more on working with strings, see the guides on Bash string concatenation and splitting strings by delimiter .
Tags
Linuxize Weekly Newsletter
A quick weekly roundup of new tutorials, news, and tips.
About the authors

Dejan Panovski
Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.
View author page