字符串操作:切片、替换、匹配 (String Operations)
章节概述
本章深入讲解 Bash 的字符串操作能力:长度计算、子串切片、模式替换、前缀/后缀删除,以及命令替换。掌握这些技巧可大幅减少外部工具(如 sed/awk)的依赖。
核心理念:Bash 的字符串操作全部通过
${var}语法完成——无需外部进程,高效且内建。
第1节:字符串长度
str="Hello, Bash!"
echo ${#str} # 12
echo ${#str[0]} # 12(数组元素长度)C 对比:
char str[] = "Hello, Bash!";
printf("%lu\n", strlen(str)); // 12# Bash 无需调用外部命令 strlen
len=${#str} # 内建操作,零进程开销第2节:子串切片 ${var:pos:len}
str="Hello, World!"
echo ${str:0:5} # Hello(从0开始取5个)
echo ${str:7} # World!(从7到末尾)
echo ${str: -6} # World!(负数需空格,从倒数第6个开始)
echo ${str: -1} # !(最后一个字符)切片速查:
| 语法 | 说明 | 示例 | 结果 |
|---|---|---|---|
${var:pos} | 从 pos 到末尾 | ${str:7} | World! |
${var:pos:len} | 从 pos 取 len 个 | ${str:0:5} | Hello |
${var: -N} | 倒数第 N 个起 | ${str: -6} | World! |
C 对比:
char str[] = "Hello, World!";
printf("%.5s\n", str); // Hello
printf("%s\n", str + 7); // World!第3节:模式替换 ${var/pattern/replacement}
str="hello world world"
# 替换第一个匹配
echo ${str/world/Shell} # hello Shell world
# 替换所有匹配
echo ${str//world/Shell} # hello Shell Shell
# 删除第一个匹配(替换为空)
echo ${str/world/} # hello world
# 删除所有匹配
echo ${str//world/} # hello替换速查:
| 语法 | 说明 |
|---|---|
${var/pattern/repl} | 替换第一个 |
${var//pattern/repl} | 替换所有 |
${var/#pattern/repl} | 替换开头 |
${var/%pattern/repl} | 替换结尾 |
第4节:前缀删除 ${var#pattern}
filepath="/home/user/documents/report.txt"
# 删除最长匹配的前缀
echo ${filepath##*/} # report.txt(删除到最后一个 /)
# 删除最短匹配的前缀
echo ${filepath#*/} # home/user/documents/report.txt
# 删除最长匹配的后缀
echo ${filepath%%.*} # /home/user/documents/report(删除到最后一个 .)
# 删除最短匹配的后缀
echo ${filepath%.*} # /home/user/documents/report(删除到第一个 .)前缀/后缀删除速查:
| 语法 | 方向 | 匹配策略 | 示例 | 结果 |
|---|---|---|---|---|
${var#pattern} | 前缀 | 最短匹配 | ${str#*/} | 保留尾部 |
${var##pattern} | 前缀 | 最长匹配 | ${str##*/} | 文件名 |
${var%pattern} | 后缀 | 最短匹配 | ${str%.*} | 路径不含扩展名 |
${var%%pattern} | 后缀 | 最长匹配 | ${str%%.*} | 主路径 |
实用示例:提取文件名和扩展名
file="/path/to/archive.tar.gz"
echo ${file##*/} # archive.tar.gz(文件名)
echo ${file%/*} # /path/to(目录)
echo ${file##*.} # gz(扩展名)
echo ${file%.*.*} # /path/to/archive(不含两级扩展名)第5节:默认值与错误处理
# ${var:-default}:变量为空时使用默认值
name=""
echo ${name:-"匿名"} # 匿名
# ${var:=default}:变量为空时赋默认值
name=""
echo ${name:="匿名"} # 匿名
echo $name # 匿名(变量已修改)
# ${var:+alternative}:变量非空时使用替代值
name="Alice"
echo ${name:+"已登录"} # 已登录
# ${var:?error_msg}:变量为空时报错退出
name=""
echo ${name:?"名字不能为空"} # 报错并退出默认值速查:
| 语法 | 说明 |
|---|---|
${var:-default} | 为空则返回 default |
${var:=default} | 为空则赋值 default |
${var:+alt} | 非空则返回 alt |
${var:?msg} | 为空则报错 msg |
第6节:大小写转换(Bash 4+)
str="Hello World"
echo ${str^^} # HELLO WORLD(全大写)
echo ${str,,} # hello world(全小写)
echo ${str^} # Hello World(首字母大写)
echo ${str,} # hello World(首字母小写)
# 部分转换
echo ${str^^[0]} # HELLO World(仅第0个字符大写)第7节:命令替换与进程替换
# 命令替换:$(cmd)
files=$(ls -1)
count=$(wc -l < /etc/passwd)
today=$(date +%Y-%m-%d)
# 进程替换:<(cmd) 和 >(cmd)
diff <(ls dir1) <(ls dir2) # 比较两个目录的文件列表命令替换 vs 进程替换:
| 语法 | 输出 | 用途 |
|---|---|---|
$(cmd) | 捕获 stdout 为字符串 | 赋值、条件 |
<(cmd) | 文件描述符路径 | 作为文件参数 |
第8节:实战 —— 路径处理函数
basename() {
local path=$1
echo ${path##*/}
}
dirname() {
local path=$1
echo ${path%/*}
}
extension() {
local file=$1
echo ${file##*.}
}
# 使用
f="/home/user/report.txt"
echo "文件名: $(basename $f)" # report.txt
echo "目录: $(dirname $f)" # /home/user
echo "扩展名: $(extension $f)" # txt第9节:课后练习
- 编写函数提取 URL 的域名部分(如
https://example.com/path→example.com) - 实现字符串反转函数
- 用
${var//pattern/}去除字符串中所有数字
相关章节: