0%

字符串操作

Shell 字符串操作:切片、长度与替换技巧

在 Shell 脚本中,字符串处理是日常任务的重要组成部分,包括提取子串、计算长度、替换内容等。掌握这些操作能帮助你高效处理文本数据,如日志分析、配置解析等。本文详细讲解 Shell 中常用的字符串操作方法。

字符串切片:提取子字符串

字符串切片用于从指定位置开始提取部分字符,Shell 提供了两种灵活的切片语法,下标从 0 开始(第一个字符为位置 0)。

从指定位置截取到结尾

语法${变量:起始位置}

  • 起始位置 开始,截取到字符串末尾的所有字符。
  • 起始位置 为负数(需用括号包裹),表示从字符串末尾倒数计算。

示例

1
2
3
4
5
6
7
8
9
10
str="abcdefgh"

# 从位置 2 开始截取(包含位置 2)
echo ${str:2} # 输出:cdefgh

# 从位置 5 开始截取
echo ${str:5} # 输出:fgh

# 从末尾倒数第 3 个位置开始(等价于长度-3)
echo ${str:(-3)} # 输出:fgh(注意负数需用括号)

截取指定长度的子串

语法${变量:起始位置:长度}

  • 起始位置 开始,截取指定 长度 的字符(若剩余字符不足,则截取到结尾)。

示例

1
2
3
4
5
6
7
8
9
10
str="123456789"

# 从位置 2 开始,截取 4 个字符
echo ${str:2:4} # 输出:3456

# 从位置 5 开始,截取 10 个字符(实际不足,截取到结尾)
echo ${str:5:10} # 输出:6789

# 从倒数第 4 个位置开始,截取 2 个字符
echo ${str:(-4):2} # 输出:67

注意:若起始位置超过字符串长度,返回空值;若长度为负数,在 Bash 4.2+ 中表示 “截取到距离末尾 长度 个字符的位置”。

字符串长度:计算字符个数

使用 ${#变量名} 可获取字符串的长度(字符个数),适用于检查输入合法性、限制字符数量等场景。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 普通字符串
name="Shell"
echo ${#name} # 输出:5

# 含空格的字符串
sentence="Hello World"
echo ${#sentence} # 输出:11(包含空格)

# 空字符串
empty=""
echo ${#empty} # 输出:0

# 实际应用:检查输入长度
read -p "请输入密码(至少6位):" passwd
if [ ${#passwd} -lt 6 ]; then
echo "密码长度不足!"
fi

字符串替换:修改匹配内容

Shell 支持两种替换模式:替换第一个匹配项和替换所有匹配项,无需依赖 sed 等外部工具。

替换第一个匹配项

语法${变量/旧字符串/新字符串}

  • 仅替换字符串中第一个旧字符串 匹配的部分。

示例

1
2
3
4
5
6
7
path="/usr/local/bin:/usr/bin:/bin"

# 替换第一个 ":" 为 ";"
echo ${path/:/;} # 输出:/usr/local/bin;/usr/bin:/bin

# 替换第一个 "usr" 为 "USER"
echo ${path/usr/USER} # 输出:/USER/local/bin:/usr/bin:/bin

替换所有匹配项

语法${变量//旧字符串/新字符串}

  • 替换字符串中所有旧字符串 匹配的部分(双斜杠 // 表示全局替换)。

示例

1
2
3
4
5
6
7
8
str="a,b,c,d"

# 替换所有 "," 为 "|"
echo ${str//,/|} # 输出:a|b|c|d

# 替换所有 "a" 为空(删除所有 "a")
text="abacada"
echo ${text//a/} # 输出:bc d(连续的a被删除)

条件性替换

  • 替换开头匹配项${变量/#旧字符串/新字符串}(仅替换字符串开头的匹配项)。
  • 替换结尾匹配项${变量/%旧字符串/新字符串}(仅替换字符串结尾的匹配项)。

示例

1
2
3
4
5
6
7
file="report.txt.bak"

# 替换开头的 "report" 为 "log"
echo ${file/#report/log} # 输出:log.txt.bak

# 替换结尾的 "bak" 为 "backup"
echo ${file/%bak/backup} # 输出:report.txt.backup

实际应用案例

解析文件路径

1
2
3
4
5
6
7
8
9
10
11
12
13
file_path="/home/user/docs/report.pdf"

# 提取文件名(含扩展名)
filename=${file_path##*/}
echo "文件名:$filename" # 输出:report.pdf

# 提取扩展名(利用替换)
ext=${filename#*.}
echo "扩展名:$ext" # 输出:pdf

# 提取文件名(不含扩展名)
name=${filename%.*}
echo "纯文件名:$name" # 输出:report

(注:### 是贪婪 / 非贪婪删除前缀的操作,%%% 是删除后缀的操作,本质是特殊的替换形式)

处理用户输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# 标准化用户输入的姓名(首字母大写,其余小写)

read -p "请输入姓名:" name

# 提取首字母并大写(结合 tr 命令)
first=${name:0:1}
first_upper=$(echo $first | tr 'a-z' 'A-Z')

# 提取剩余字符并小写
rest=${name:1}
rest_lower=$(echo $rest | tr 'A-Z' 'a-z')

# 合并结果
standard_name="$first_upper$rest_lower"
echo "标准化姓名:$standard_name"

执行效果:

1
2
请输入姓名:jOhN
标准化姓名:John

欢迎关注我的其它发布渠道