重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
string-manipulation-mastery by josiahsiegel/claude-plugin-marketplace
npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill string-manipulation-mastery强制要求:在 Windows 上始终对文件路径使用反斜杠
在 Windows 上使用编辑或写入工具时,必须在文件路径中使用反斜杠(\),而不是正斜杠(/)。
使用参数扩展、模式匹配、正则表达式和内置转换在 bash 中进行字符串操作的综合指南。掌握这些技术,以避免为简单操作而生成外部进程,如 sed、awk 或 cut。
#!/usr/bin/env bash
set -euo pipefail
str="Hello, World!"
# 获取长度
echo "${#str}" # 13
# 数组元素的长度
arr=("short" "much longer string")
echo "${#arr[0]}" # 5
echo "${#arr[1]}" # 18
# 数组元素的数量(非字符串长度)
echo "${#arr[@]}" # 2
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
#!/usr/bin/env bash
set -euo pipefail
str="Hello, World!"
# ${var:offset} - 从偏移量到末尾
echo "${str:7}" # World!
# ${var:offset:length} - 从偏移量开始,长度为 length 个字符
echo "${str:0:5}" # Hello
echo "${str:7:5}" # World
# 负偏移量(从末尾开始)- 注意空格或括号
echo "${str: -6}" # World!
echo "${str:(-6)}" # World!
echo "${str: -6:5}" # World
# 最后 N 个字符
last_n() {
local str="$1" n="$2"
echo "${str: -$n}"
}
last_n "Hello" 3 # llo
# 提取位置之间的内容
between() {
local str="$1" start="$2" end="$3"
echo "${str:start:$((end - start))}"
}
between "0123456789" 3 7 # 3456
#!/usr/bin/env bash
set -euo pipefail
# ${var:-default} - 如果未设置或为空,则使用默认值
name="${1:-Anonymous}"
echo "Hello, $name"
# ${var:=default} - 如果未设置或为空,则分配默认值
: "${CONFIG_FILE:=/etc/app.conf}"
# ${var:+alternate} - 如果变量已设置,则使用替代值
debug_flag="${DEBUG:+--verbose}"
# ${var:?error} - 如果未设置或为空,则退出并报错
: "${REQUIRED_VAR:?REQUIRED_VAR must be set}"
# 不带冒号 - 仅检查是否未设置(允许为空)
# ${var-default}, ${var=default}, ${var+alt}, ${var?err}
# 实际示例:带默认值的配置
setup_config() {
: "${DB_HOST:=localhost}"
: "${DB_PORT:=5432}"
: "${DB_NAME:=myapp}"
: "${DB_USER:=postgres}"
}
#!/usr/bin/env bash
set -euo pipefail
# ${!var} - 间接引用
config_host="server.example.com"
config_port="8080"
key="config_host"
echo "${!key}" # server.example.com
# 遍历相关变量
for suffix in host port; do
var="config_$suffix"
echo "$suffix = ${!var}"
done
# 获取匹配模式的所有变量名
# ${!prefix@} 或 ${!prefix*}
for var in "${!config_@}"; do
echo "$var = ${!var}"
done
# 数组间接引用
arr=(a b c d e)
idx=2
echo "${arr[$idx]}" # c
# 间接数组引用(Bash 4.3+ 名称引用)
get_array_element() {
local -n arr_ref="$1"
local idx="$2"
echo "${arr_ref[$idx]}"
}
get_array_element arr 3 # d
#!/usr/bin/env bash
set -euo pipefail
path="/home/user/documents/file.tar.gz"
# ${var#pattern} - 移除最短的前缀匹配
echo "${path#*/}" # home/user/documents/file.tar.gz
# ${var##pattern} - 移除最长的前缀匹配
echo "${path##*/}" # file.tar.gz (basename)
# 移除扩展名
filename="archive.tar.gz"
echo "${filename#*.}" # tar.gz (第一个 . 之后)
echo "${filename##*.}" # gz (仅最后一个扩展名)
# 移除前缀字符串
url="https://example.com/path"
echo "${url#https://}" # example.com/path
# 实际应用:获取文件扩展名
get_extension() {
local file="$1"
echo "${file##*.}"
}
# 实际应用:去除前导零
strip_leading_zeros() {
local num="$1"
echo "${num#"${num%%[!0]*}"}"
}
strip_leading_zeros "000123" # 123
#!/usr/bin/env bash
set -euo pipefail
path="/home/user/documents/file.tar.gz"
# ${var%pattern} - 移除最短的后缀匹配
echo "${path%/*}" # /home/user/documents (dirname)
# ${var%%pattern} - 移除最长的后缀匹配
echo "${path%%/*}" # (空 - 移除所有内容)
# 移除扩展名
filename="archive.tar.gz"
echo "${filename%.*}" # archive.tar
echo "${filename%%.*}" # archive
# 实际应用:获取目录
dirname="${path%/*}"
# 实际应用:移除文件扩展名
basename="${path##*/}"
name_without_ext="${basename%.*}"
# 组合使用:更改扩展名
change_extension() {
local file="$1" new_ext="$2"
echo "${file%.*}.$new_ext"
}
change_extension "doc.txt" "md" # doc.md
#!/usr/bin/env bash
set -euo pipefail
str="hello hello hello"
# ${var/pattern/replacement} - 替换第一个匹配项
echo "${str/hello/hi}" # hi hello hello
# ${var//pattern/replacement} - 替换所有匹配项
echo "${str//hello/hi}" # hi hi hi
# ${var/#pattern/replacement} - 如果位于开头则替换
echo "${str/#hello/hi}" # hi hello hello
# ${var/%pattern/replacement} - 如果位于末尾则替换
echo "${str/%hello/goodbye}" # hello hello goodbye
# 删除模式(空替换)
echo "${str//hello/}" # " " (仅空格)
# 实际应用:清理文件名
sanitize_filename() {
local name="$1"
# 将空格替换为下划线
name="${name// /_}"
# 移除特殊字符
name="${name//[^a-zA-Z0-9._-]/}"
echo "$name"
}
sanitize_filename "My File (2024).txt" # My_File_2024.txt
# 实际应用:路径操作
normalize_path() {
local path="$1"
# 移除双斜杠
while [[ "$path" == *//* ]]; do
path="${path//\/\//\/}"
done
# 移除尾部斜杠
echo "${path%/}"
}
#!/usr/bin/env bash
set -euo pipefail
str="Hello World"
# 首字符小写
echo "${str,}" # hello World
# 全部小写
echo "${str,,}" # hello world
# 首字符大写
echo "${str^}" # Hello World (已大写)
str2="hello world"
echo "${str2^}" # Hello world
# 全部大写
echo "${str,,}" # hello world
echo "${str2^^}" # HELLO WORLD
# 切换大小写(Bash 4.4+)
echo "${str~~}" # hELLO wORLD
#!/usr/bin/env bash
set -euo pipefail
str="hello world"
# 仅将匹配模式转换为大写
echo "${str^^[aeiou]}" # hEllO wOrld
# 仅将匹配模式转换为小写
str2="HELLO WORLD"
echo "${str2,,[AEIOU]}" # HeLLo WoRLD
# 实际应用:标题大小写
title_case() {
local str="$1"
local result=""
local capitalize=true
for ((i=0; i<${#str}; i++)); do
local char="${str:$i:1}"
if [[ "$char" == " " ]]; then
result+="$char"
capitalize=true
elif $capitalize; then
result+="${char^}"
capitalize=false
else
result+="${char,}"
fi
done
echo "$result"
}
title_case "hello WORLD from BASH" # Hello World From Bash
#!/usr/bin/env bash
set -euo pipefail
# =~ 运算符用于正则表达式匹配
str="Hello World 123"
if [[ "$str" =~ ^Hello ]]; then
echo "Starts with Hello"
fi
if [[ "$str" =~ [0-9]+ ]]; then
echo "Contains numbers"
fi
# 使用 BASH_REMATCH 捕获组
email="user@example.com"
if [[ "$email" =~ ^([^@]+)@(.+)$ ]]; then
echo "User: ${BASH_REMATCH[1]}" # user
echo "Domain: ${BASH_REMATCH[2]}" # example.com
echo "Full match: ${BASH_REMATCH[0]}" # user@example.com
fi
# 将正则表达式存储在变量中(避免引用问题)
pattern='^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
date="2024-03-15"
if [[ "$date" =~ $pattern ]]; then
echo "Valid date format"
fi
# 多个捕获组
log_line='2024-03-15 10:30:45 ERROR Connection failed'
pattern='^([0-9-]+) ([0-9:]+) ([A-Z]+) (.+)$'
if [[ "$log_line" =~ $pattern ]]; then
date="${BASH_REMATCH[1]}"
time="${BASH_REMATCH[2]}"
level="${BASH_REMATCH[3]}"
message="${BASH_REMATCH[4]}"
fi
#!/usr/bin/env bash
set -euo pipefail
# 电子邮件验证
is_valid_email() {
local email="$1"
local pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
[[ "$email" =~ $pattern ]]
}
# IP 地址验证
is_valid_ip() {
local ip="$1"
local octet='(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
local pattern="^${octet}\.${octet}\.${octet}\.${octet}$"
[[ "$ip" =~ $pattern ]]
}
# URL 解析
parse_url() {
local url="$1"
local pattern='^(https?|ftp)://([^/:]+)(:([0-9]+))?(/.*)?$'
if [[ "$url" =~ $pattern ]]; then
echo "Protocol: ${BASH_REMATCH[1]}"
echo "Host: ${BASH_REMATCH[2]}"
echo "Port: ${BASH_REMATCH[4]:-default}"
echo "Path: ${BASH_REMATCH[5]:-/}"
else
echo "Invalid URL" >&2
return 1
fi
}
# 语义化版本解析
parse_semver() {
local version="$1"
local pattern='^v?([0-9]+)\.([0-9]+)\.([0-9]+)(-([a-zA-Z0-9.-]+))?(\+([a-zA-Z0-9.-]+))?$'
if [[ "$version" =~ $pattern ]]; then
echo "Major: ${BASH_REMATCH[1]}"
echo "Minor: ${BASH_REMATCH[2]}"
echo "Patch: ${BASH_REMATCH[3]}"
echo "Prerelease: ${BASH_REMATCH[5]:-none}"
echo "Build: ${BASH_REMATCH[7]:-none}"
fi
}
#!/usr/bin/env bash
set -euo pipefail
# 使用 IFS 和 read
str="one,two,three,four"
IFS=',' read -ra arr <<< "$str"
echo "${arr[0]}" # one
echo "${arr[2]}" # three
# 多字符分隔符(使用参数扩展)
str="one||two||three"
arr=()
while [[ "$str" == *"||"* ]]; do
arr+=("${str%%||*}")
str="${str#*||}"
done
arr+=("$str")
# 使用 mapfile(换行符分隔)
mapfile -t lines <<< "$(echo -e "line1\nline2\nline3")"
# 按任意空白字符分割
str="one two three"
read -ra arr <<< "$str" # IFS 默认为空白字符
#!/usr/bin/env bash
set -euo pipefail
arr=("one" "two" "three" "four")
# 使用 IFS 和分隔符连接
join_by() {
local IFS="$1"
shift
echo "$*"
}
join_by ',' "${arr[@]}" # one,two,three,four
join_by ' | ' "${arr[@]}" # one | two | three | four
# 使用 printf 的替代方法
join_array() {
local delim="$1"
shift
local first="$1"
shift
printf '%s' "$first" "${@/#/$delim}"
}
join_array ',' "${arr[@]}"
# 使用自定义格式连接
printf '"%s" ' "${arr[@]}" # "one" "two" "three" "four"
printf '%s\n' "${arr[@]}" # 每行一个
#!/usr/bin/env bash
set -euo pipefail
# 修剪前导空白字符
trim_leading() {
local str="$1"
echo "${str#"${str%%[![:space:]]*}"}"
}
# 修剪尾部空白字符
trim_trailing() {
local str="$1"
echo "${str%"${str##*[![:space:]]}"}"
}
# 修剪两端
trim() {
local str="$1"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
echo "$str"
}
# 扩展模式匹配版本(需要 shopt -s extglob)
trim_extglob() {
shopt -s extglob
local str="$1"
str="${str##+([[:space:]])}"
str="${str%%+([[:space:]])}"
echo "$str"
}
str=" hello world "
trim "$str" # "hello world"
#!/usr/bin/env bash
set -euo pipefail
# 重复字符串 N 次
repeat() {
local str="$1"
local n="$2"
local result=""
for ((i=0; i<n; i++)); do
result+="$str"
done
echo "$result"
}
repeat "ab" 5 # ababababab
# 使用 printf
repeat_printf() {
local str="$1"
local n="$2"
printf '%s' $(printf '%.0s'"$str" $(seq 1 "$n"))
}
# 创建分隔线
separator() {
local char="${1:--}"
local width="${2:-80}"
printf '%*s\n' "$width" '' | tr ' ' "$char"
}
separator "=" 40 # ========================================
#!/usr/bin/env bash
set -euo pipefail
str="hello world"
# 通过扩展实现类似 tr 的替换
# 将所有 'l' 替换为 'L'
echo "${str//l/L}" # heLLo worLd
# 删除字符
echo "${str//o/}" # hell wrld
# 逐个字符翻译
translate() {
local str="$1"
local from="$2"
local to="$3"
for ((i=0; i<${#from}; i++)); do
str="${str//${from:$i:1}/${to:$i:1}}"
done
echo "$str"
}
translate "hello" "el" "ip" # hippo
#!/usr/bin/env bash
set -euo pipefail
# 右填充至指定宽度
pad_right() {
local str="$1"
local width="$2"
local char="${3:- }"
printf "%-${width}s" "$str" | tr ' ' "$char"
}
# 左填充至指定宽度
pad_left() {
local str="$1"
local width="$2"
local char="${3:- }"
printf "%${width}s" "$str" | tr ' ' "$char"
}
# 居中对齐
center() {
local str="$1"
local width="$2"
local len=${#str}
local padding=$(( (width - len) / 2 ))
printf "%*s%s%*s" $padding "" "$str" $((width - len - padding)) ""
}
# 数字零填充
zero_pad() {
local num="$1"
local width="$2"
printf "%0${width}d" "$num"
}
zero_pad 42 5 # 00042
# 格式化表格
print_table_row() {
printf "| %-20s | %10s | %-15s |\n" "$1" "$2" "$3"
}
print_table_row "Name" "Age" "City"
print_table_row "Alice" "30" "New York"
#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob
# 扩展模式:
# ?(pattern) - 0 或 1 次出现
# *(pattern) - 0 次或多次出现
# +(pattern) - 1 次或多次出现
# @(pattern) - 恰好出现 1 次
# !(pattern) - 除模式之外的任何内容
# 匹配文件
ls *.@(jpg|png|gif) # 仅图像文件
ls !(*.bak|*.tmp) # 排除备份/临时文件
ls +([0-9]).txt # 以数字开头的文件
# 字符串操作
str=" hello world "
# 移除前导空白字符
echo "${str##+([[:space:]])}" # "hello world "
# 移除所有空白字符
echo "${str//+([[:space:]])/ }" # " hello world "
# 移除多个扩展名
file="archive.tar.gz.bak"
echo "${file%.@(tar|gz|bak)*}" # archive
# 匹配备选项
case "$response" in
@(yes|y|Y|YES))
echo "Affirmative"
;;
@(no|n|N|NO))
echo "Negative"
;;
esac
#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob
# 清理备份文件
rm -f *.@(bak|backup|orig|~)
# 仅查找源文件
ls *.@(c|cpp|h|hpp|cc)
# 排除某些模式
for file in !(test_*|_*).py; do
process "$file"
done
# 匹配复杂的版本字符串
version_pattern='+([0-9]).+([0-9]).+([0-9])?(-+([a-z0-9]))'
if [[ "$version" == $version_pattern ]]; then
echo "Valid version"
fi
# 移除冗余字符
clean_string() {
local str="$1"
# 移除重复的空格
echo "${str//+([[:space:]])/ }"
}
# 匹配可选部分
file_pattern='*.@(test|spec)?.@(js|ts)'
# 匹配:file.js, file.ts, file.test.js, file.spec.ts 等
#!/usr/bin/env bash
# 需要 Bash 5.3+
set -euo pipefail
# 无需子进程的字符串操作
result=${ echo "${str^^}"; } # 无需子进程的大写转换
# 用于字符串构建的 REPLY 语法
build_path() {
local parts=("$@")
REPLY=""
for part in "${parts[@]}"; do
${| REPLY+="${REPLY:+/}$part"; }
done
}
# 高效的字符串累积
accumulate() {
local -n result="$1"
shift
for item in "$@"; do
${| result+="$item"; }
done
}
#!/usr/bin/env bash
set -euo pipefail
str="hello world"
# ✗ 慢 - 生成外部进程
basename=$(basename "$path")
dirname=$(dirname "$path")
upper=$(echo "$str" | tr 'a-z' 'A-Z')
len=$(echo -n "$str" | wc -c)
# ✓ 快 - 纯 bash
basename="${path##*/}"
dirname="${path%/*}"
upper="${str^^}"
len="${#str}"
#!/usr/bin/env bash
set -euo pipefail
# ✗ 慢 - 多次扩展
str="$input"
str="${str// / }"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
str="${str,,}"
# ✓ 更好 - 单次函数调用
normalize_string() {
local str="$1"
str="${str// / }"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
echo "${str,,}"
}
result=$(normalize_string "$input")
掌握 bash 字符串操作,编写无需外部依赖的高效脚本。
每周安装次数
61
仓库
GitHub 星标数
21
首次出现
2026年1月24日
安全审计
安装于
claude-code48
gemini-cli47
opencode47
codex45
cursor43
github-copilot40
MANDATORY: Always Use Backslashes on Windows for File Paths
When using Edit or Write tools on Windows, you MUST use backslashes (\) in file paths, NOT forward slashes (/).
Comprehensive guide to string manipulation in bash using parameter expansion, pattern matching, regular expressions, and built-in transformations. Master these techniques to avoid spawning external processes like sed, awk, or cut for simple operations.
#!/usr/bin/env bash
set -euo pipefail
str="Hello, World!"
# Get length
echo "${#str}" # 13
# Length of array element
arr=("short" "much longer string")
echo "${#arr[0]}" # 5
echo "${#arr[1]}" # 18
# Number of array elements (not string length)
echo "${#arr[@]}" # 2
#!/usr/bin/env bash
set -euo pipefail
str="Hello, World!"
# ${var:offset} - from offset to end
echo "${str:7}" # World!
# ${var:offset:length} - from offset, length chars
echo "${str:0:5}" # Hello
echo "${str:7:5}" # World
# Negative offset (from end) - note the space or parentheses
echo "${str: -6}" # World!
echo "${str:(-6)}" # World!
echo "${str: -6:5}" # World
# Last N characters
last_n() {
local str="$1" n="$2"
echo "${str: -$n}"
}
last_n "Hello" 3 # llo
# Extract between positions
between() {
local str="$1" start="$2" end="$3"
echo "${str:start:$((end - start))}"
}
between "0123456789" 3 7 # 3456
#!/usr/bin/env bash
set -euo pipefail
# ${var:-default} - use default if unset or empty
name="${1:-Anonymous}"
echo "Hello, $name"
# ${var:=default} - assign default if unset or empty
: "${CONFIG_FILE:=/etc/app.conf}"
# ${var:+alternate} - use alternate if var IS set
debug_flag="${DEBUG:+--verbose}"
# ${var:?error} - exit with error if unset or empty
: "${REQUIRED_VAR:?REQUIRED_VAR must be set}"
# Without colon - only checks if unset (empty is OK)
# ${var-default}, ${var=default}, ${var+alt}, ${var?err}
# Practical example: config with defaults
setup_config() {
: "${DB_HOST:=localhost}"
: "${DB_PORT:=5432}"
: "${DB_NAME:=myapp}"
: "${DB_USER:=postgres}"
}
#!/usr/bin/env bash
set -euo pipefail
# ${!var} - indirect reference
config_host="server.example.com"
config_port="8080"
key="config_host"
echo "${!key}" # server.example.com
# Iterate over related variables
for suffix in host port; do
var="config_$suffix"
echo "$suffix = ${!var}"
done
# Get all variable names matching pattern
# ${!prefix@} or ${!prefix*}
for var in "${!config_@}"; do
echo "$var = ${!var}"
done
# Array indirection
arr=(a b c d e)
idx=2
echo "${arr[$idx]}" # c
# Indirect array reference (Bash 4.3+ nameref)
get_array_element() {
local -n arr_ref="$1"
local idx="$2"
echo "${arr_ref[$idx]}"
}
get_array_element arr 3 # d
#!/usr/bin/env bash
set -euo pipefail
path="/home/user/documents/file.tar.gz"
# ${var#pattern} - remove shortest prefix match
echo "${path#*/}" # home/user/documents/file.tar.gz
# ${var##pattern} - remove longest prefix match
echo "${path##*/}" # file.tar.gz (basename)
# Remove extension
filename="archive.tar.gz"
echo "${filename#*.}" # tar.gz (first . onwards)
echo "${filename##*.}" # gz (last extension only)
# Remove prefix string
url="https://example.com/path"
echo "${url#https://}" # example.com/path
# Practical: Get file extension
get_extension() {
local file="$1"
echo "${file##*.}"
}
# Practical: Strip leading zeros
strip_leading_zeros() {
local num="$1"
echo "${num#"${num%%[!0]*}"}"
}
strip_leading_zeros "000123" # 123
#!/usr/bin/env bash
set -euo pipefail
path="/home/user/documents/file.tar.gz"
# ${var%pattern} - remove shortest suffix match
echo "${path%/*}" # /home/user/documents (dirname)
# ${var%%pattern} - remove longest suffix match
echo "${path%%/*}" # (empty - removes everything)
# Remove extension
filename="archive.tar.gz"
echo "${filename%.*}" # archive.tar
echo "${filename%%.*}" # archive
# Practical: Get directory
dirname="${path%/*}"
# Practical: Remove file extension
basename="${path##*/}"
name_without_ext="${basename%.*}"
# Combined: Change extension
change_extension() {
local file="$1" new_ext="$2"
echo "${file%.*}.$new_ext"
}
change_extension "doc.txt" "md" # doc.md
#!/usr/bin/env bash
set -euo pipefail
str="hello hello hello"
# ${var/pattern/replacement} - replace first match
echo "${str/hello/hi}" # hi hello hello
# ${var//pattern/replacement} - replace all matches
echo "${str//hello/hi}" # hi hi hi
# ${var/#pattern/replacement} - replace if at start
echo "${str/#hello/hi}" # hi hello hello
# ${var/%pattern/replacement} - replace if at end
echo "${str/%hello/goodbye}" # hello hello goodbye
# Delete pattern (empty replacement)
echo "${str//hello/}" # " " (just spaces)
# Practical: Sanitize filename
sanitize_filename() {
local name="$1"
# Replace spaces with underscores
name="${name// /_}"
# Remove special characters
name="${name//[^a-zA-Z0-9._-]/}"
echo "$name"
}
sanitize_filename "My File (2024).txt" # My_File_2024.txt
# Practical: Path manipulation
normalize_path() {
local path="$1"
# Remove double slashes
while [[ "$path" == *//* ]]; do
path="${path//\/\//\/}"
done
# Remove trailing slash
echo "${path%/}"
}
#!/usr/bin/env bash
set -euo pipefail
str="Hello World"
# Lowercase first character
echo "${str,}" # hello World
# Lowercase all
echo "${str,,}" # hello world
# Uppercase first character
echo "${str^}" # Hello World (already uppercase)
str2="hello world"
echo "${str2^}" # Hello world
# Uppercase all
echo "${str,,}" # hello world
echo "${str2^^}" # HELLO WORLD
# Toggle case (Bash 4.4+)
echo "${str~~}" # hELLO wORLD
#!/usr/bin/env bash
set -euo pipefail
str="hello world"
# Uppercase only matching pattern
echo "${str^^[aeiou]}" # hEllO wOrld
# Lowercase only matching pattern
str2="HELLO WORLD"
echo "${str2,,[AEIOU]}" # HeLLo WoRLD
# Practical: Title case
title_case() {
local str="$1"
local result=""
local capitalize=true
for ((i=0; i<${#str}; i++)); do
local char="${str:$i:1}"
if [[ "$char" == " " ]]; then
result+="$char"
capitalize=true
elif $capitalize; then
result+="${char^}"
capitalize=false
else
result+="${char,}"
fi
done
echo "$result"
}
title_case "hello WORLD from BASH" # Hello World From Bash
#!/usr/bin/env bash
set -euo pipefail
# =~ operator for regex matching
str="Hello World 123"
if [[ "$str" =~ ^Hello ]]; then
echo "Starts with Hello"
fi
if [[ "$str" =~ [0-9]+ ]]; then
echo "Contains numbers"
fi
# Capture groups with BASH_REMATCH
email="user@example.com"
if [[ "$email" =~ ^([^@]+)@(.+)$ ]]; then
echo "User: ${BASH_REMATCH[1]}" # user
echo "Domain: ${BASH_REMATCH[2]}" # example.com
echo "Full match: ${BASH_REMATCH[0]}" # user@example.com
fi
# Store regex in variable (avoids quoting issues)
pattern='^[0-9]{4}-[0-9]{2}-[0-9]{2}$'
date="2024-03-15"
if [[ "$date" =~ $pattern ]]; then
echo "Valid date format"
fi
# Multiple capture groups
log_line='2024-03-15 10:30:45 ERROR Connection failed'
pattern='^([0-9-]+) ([0-9:]+) ([A-Z]+) (.+)$'
if [[ "$log_line" =~ $pattern ]]; then
date="${BASH_REMATCH[1]}"
time="${BASH_REMATCH[2]}"
level="${BASH_REMATCH[3]}"
message="${BASH_REMATCH[4]}"
fi
#!/usr/bin/env bash
set -euo pipefail
# Email validation
is_valid_email() {
local email="$1"
local pattern='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
[[ "$email" =~ $pattern ]]
}
# IP address validation
is_valid_ip() {
local ip="$1"
local octet='(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
local pattern="^${octet}\.${octet}\.${octet}\.${octet}$"
[[ "$ip" =~ $pattern ]]
}
# URL parsing
parse_url() {
local url="$1"
local pattern='^(https?|ftp)://([^/:]+)(:([0-9]+))?(/.*)?$'
if [[ "$url" =~ $pattern ]]; then
echo "Protocol: ${BASH_REMATCH[1]}"
echo "Host: ${BASH_REMATCH[2]}"
echo "Port: ${BASH_REMATCH[4]:-default}"
echo "Path: ${BASH_REMATCH[5]:-/}"
else
echo "Invalid URL" >&2
return 1
fi
}
# Semantic version parsing
parse_semver() {
local version="$1"
local pattern='^v?([0-9]+)\.([0-9]+)\.([0-9]+)(-([a-zA-Z0-9.-]+))?(\+([a-zA-Z0-9.-]+))?$'
if [[ "$version" =~ $pattern ]]; then
echo "Major: ${BASH_REMATCH[1]}"
echo "Minor: ${BASH_REMATCH[2]}"
echo "Patch: ${BASH_REMATCH[3]}"
echo "Prerelease: ${BASH_REMATCH[5]:-none}"
echo "Build: ${BASH_REMATCH[7]:-none}"
fi
}
#!/usr/bin/env bash
set -euo pipefail
# Using IFS and read
str="one,two,three,four"
IFS=',' read -ra arr <<< "$str"
echo "${arr[0]}" # one
echo "${arr[2]}" # three
# Multi-character delimiter (using parameter expansion)
str="one||two||three"
arr=()
while [[ "$str" == *"||"* ]]; do
arr+=("${str%%||*}")
str="${str#*||}"
done
arr+=("$str")
# Using mapfile (newline-delimited)
mapfile -t lines <<< "$(echo -e "line1\nline2\nline3")"
# Split on any whitespace
str="one two three"
read -ra arr <<< "$str" # IFS defaults to whitespace
#!/usr/bin/env bash
set -euo pipefail
arr=("one" "two" "three" "four")
# Join with delimiter using IFS
join_by() {
local IFS="$1"
shift
echo "$*"
}
join_by ',' "${arr[@]}" # one,two,three,four
join_by ' | ' "${arr[@]}" # one | two | three | four
# Alternative using printf
join_array() {
local delim="$1"
shift
local first="$1"
shift
printf '%s' "$first" "${@/#/$delim}"
}
join_array ',' "${arr[@]}"
# Join with custom format
printf '"%s" ' "${arr[@]}" # "one" "two" "three" "four"
printf '%s\n' "${arr[@]}" # One per line
#!/usr/bin/env bash
set -euo pipefail
# Trim leading whitespace
trim_leading() {
local str="$1"
echo "${str#"${str%%[![:space:]]*}"}"
}
# Trim trailing whitespace
trim_trailing() {
local str="$1"
echo "${str%"${str##*[![:space:]]}"}"
}
# Trim both
trim() {
local str="$1"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
echo "$str"
}
# Extended pattern matching version (requires shopt -s extglob)
trim_extglob() {
shopt -s extglob
local str="$1"
str="${str##+([[:space:]])}"
str="${str%%+([[:space:]])}"
echo "$str"
}
str=" hello world "
trim "$str" # "hello world"
#!/usr/bin/env bash
set -euo pipefail
# Repeat string N times
repeat() {
local str="$1"
local n="$2"
local result=""
for ((i=0; i<n; i++)); do
result+="$str"
done
echo "$result"
}
repeat "ab" 5 # ababababab
# Using printf
repeat_printf() {
local str="$1"
local n="$2"
printf '%s' $(printf '%.0s'"$str" $(seq 1 "$n"))
}
# Create separator line
separator() {
local char="${1:--}"
local width="${2:-80}"
printf '%*s\n' "$width" '' | tr ' ' "$char"
}
separator "=" 40 # ========================================
#!/usr/bin/env bash
set -euo pipefail
str="hello world"
# Using tr-like replacement via expansion
# Replace all 'l' with 'L'
echo "${str//l/L}" # heLLo worLd
# Delete characters
echo "${str//o/}" # hell wrld
# Translate character by character
translate() {
local str="$1"
local from="$2"
local to="$3"
for ((i=0; i<${#from}; i++)); do
str="${str//${from:$i:1}/${to:$i:1}}"
done
echo "$str"
}
translate "hello" "el" "ip" # hippo
#!/usr/bin/env bash
set -euo pipefail
# Right pad to width
pad_right() {
local str="$1"
local width="$2"
local char="${3:- }"
printf "%-${width}s" "$str" | tr ' ' "$char"
}
# Left pad to width
pad_left() {
local str="$1"
local width="$2"
local char="${3:- }"
printf "%${width}s" "$str" | tr ' ' "$char"
}
# Center align
center() {
local str="$1"
local width="$2"
local len=${#str}
local padding=$(( (width - len) / 2 ))
printf "%*s%s%*s" $padding "" "$str" $((width - len - padding)) ""
}
# Zero-pad numbers
zero_pad() {
local num="$1"
local width="$2"
printf "%0${width}d" "$num"
}
zero_pad 42 5 # 00042
# Format table
print_table_row() {
printf "| %-20s | %10s | %-15s |\n" "$1" "$2" "$3"
}
print_table_row "Name" "Age" "City"
print_table_row "Alice" "30" "New York"
#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob
# Extended patterns:
# ?(pattern) - 0 or 1 occurrence
# *(pattern) - 0 or more occurrences
# +(pattern) - 1 or more occurrences
# @(pattern) - exactly 1 occurrence
# !(pattern) - anything except pattern
# Match files
ls *.@(jpg|png|gif) # Images only
ls !(*.bak|*.tmp) # Exclude backup/temp files
ls +([0-9]).txt # Files starting with digits
# String manipulation
str=" hello world "
# Remove leading whitespace
echo "${str##+([[:space:]])}" # "hello world "
# Remove all whitespace
echo "${str//+([[:space:]])/ }" # " hello world "
# Remove multiple extensions
file="archive.tar.gz.bak"
echo "${file%.@(tar|gz|bak)*}" # archive
# Match alternatives
case "$response" in
@(yes|y|Y|YES))
echo "Affirmative"
;;
@(no|n|N|NO))
echo "Negative"
;;
esac
#!/usr/bin/env bash
set -euo pipefail
shopt -s extglob
# Clean backup files
rm -f *.@(bak|backup|orig|~)
# Find source files only
ls *.@(c|cpp|h|hpp|cc)
# Exclude certain patterns
for file in !(test_*|_*).py; do
process "$file"
done
# Match complex version strings
version_pattern='+([0-9]).+([0-9]).+([0-9])?(-+([a-z0-9]))'
if [[ "$version" == $version_pattern ]]; then
echo "Valid version"
fi
# Remove redundant characters
clean_string() {
local str="$1"
# Remove repeated spaces
echo "${str//+([[:space:]])/ }"
}
# Match optional parts
file_pattern='*.@(test|spec)?.@(js|ts)'
# Matches: file.js, file.ts, file.test.js, file.spec.ts, etc.
#!/usr/bin/env bash
# Requires Bash 5.3+
set -euo pipefail
# No-fork string operations
result=${ echo "${str^^}"; } # Uppercase without subshell
# REPLY syntax for string building
build_path() {
local parts=("$@")
REPLY=""
for part in "${parts[@]}"; do
${| REPLY+="${REPLY:+/}$part"; }
done
}
# Efficient string accumulation
accumulate() {
local -n result="$1"
shift
for item in "$@"; do
${| result+="$item"; }
done
}
#!/usr/bin/env bash
set -euo pipefail
str="hello world"
# ✗ SLOW - spawns external process
basename=$(basename "$path")
dirname=$(dirname "$path")
upper=$(echo "$str" | tr 'a-z' 'A-Z')
len=$(echo -n "$str" | wc -c)
# ✓ FAST - pure bash
basename="${path##*/}"
dirname="${path%/*}"
upper="${str^^}"
len="${#str}"
#!/usr/bin/env bash
set -euo pipefail
# ✗ SLOW - multiple expansions
str="$input"
str="${str// / }"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
str="${str,,}"
# ✓ BETTER - single function call
normalize_string() {
local str="$1"
str="${str// / }"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
echo "${str,,}"
}
result=$(normalize_string "$input")
Master bash string manipulation to write efficient scripts without external dependencies.
Weekly Installs
61
Repository
GitHub Stars
21
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code48
gemini-cli47
opencode47
codex45
cursor43
github-copilot40
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
123,700 周安装