字符串操作:切片、替换、匹配 (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节:课后练习

  1. 编写函数提取 URL 的域名部分(如 https://example.com/pathexample.com
  2. 实现字符串反转函数
  3. ${var//pattern/} 去除字符串中所有数字

相关章节: