Shell字符串操作符详解
一、操作符速查表
| 操作符 | 名称 | 作用 | 示例 | 结果 |
|---|---|---|---|---|
${#var} | 字符串长度 | 返回字符串长度 | str="hello"; echo ${#str} | 5 |
${var:m} | 从m截取到结尾 | 从第m个字符开始 | str="hello"; echo ${str:2} | llo |
${var:m:len} | 从m截取len长度 | 从m开始取len个 | str="hello"; echo ${str:1:3} | ell |
${var#pattern} | 删最短前缀 | 删除开头匹配的最短部分 | file="a.b.c"; echo ${file#*.} | b.c |
${var##pattern} | 删最长前缀 | 删除开头匹配的最长部分 | file="a.b.c"; echo ${file##*.} | c |
${var%pattern} | 删最短后缀 | 删除结尾匹配的最短部分 | file="a.b.c"; echo ${file%.*} | a.b |
${var%%pattern} | 删最长后缀 | 删除结尾匹配的最长部分 | file="a.b.c"; echo ${file%%.*} | a |
二、详细示例
2.1 ${#var} – 字符串长度
str="Hello World"
echo "长度: ${#str}" # 输出: 11
name="张三"
echo "姓名长度: ${#name}" # 输出: 2(中文字符算1个)
# 数组长度
arr=("a" "b" "c")
echo "数组长度: ${#arr[@]}" # 输出: 3
echo "第一个元素长度: ${#arr[0]}" # 输出: 1

2.2 ${var:m} – 从位置m到结尾
str="Hello World"
echo "${str:6}" # 输出: World(从第6个字符开始)
# 支持负数(从后往前)
echo "${str: -5}" # 输出: World(最后5个字符)
# 注意:-5前面要有空格,或者用括号
echo "${str: -5}" # World
echo "${str:(-5)}" # World
# 实际应用:获取文件扩展名
filename="document.pdf"
echo "${filename: -4}" # 输出: .pdf(但可能不准确)

2.3 ${var:m:len} – 从m开始取len长度
#!/bin/bash
str="Hello World"
echo "${str:0:5}" # 输出: Hello
echo "${str:6:3}" # 输出: Wor
# 提取日期
date="2000-12-20"
year="${date:0:4}"
month="${date:5:2}"
day="${date:8:2}"
echo "年: $year, 月: $month, 日: $day"
# 输出: 年: 2000, 月: 12, 日: 20

2.4 ${var#pattern} – 删除最短前缀
# # 从开头删除,最短匹配
filename="/usr/local/bin/script.sh"
echo "${filename#*/}" # 输出: usr/local/bin/script.sh(删除第一个/)
echo "${filename#/*/}" # 输出: local/bin/script.sh(删除/usr/)
# 提取文件名(删除路径)
fullpath="/home/user/docs/file.txt"
echo "${fullpath##*/}" # 用##提取文件名(最长匹配)
# 输出: file.txt

2.5 ${var##pattern} – 删除最长前缀
# ## 从开头删除,最长匹配
filename="/usr/local/bin/script.sh"
echo "${filename##*/}" # 输出: script.sh(删除所有路径)
# 提取协议
url="https://example.com/page"
echo "${url##*://}" # 输出: example.com/page
2.6 ${var%pattern} – 删除最短后缀
# % 从结尾删除,最短匹配
filename="document.pdf"
echo "${filename%.*}" # 输出: document(删除最短后缀)
# 多级扩展名
file="archive.tar.gz"
echo "${file%.*}" # 输出: archive.tar(删除.gz)

2.7 ${var%%pattern} – 删除最长后缀
# %% 从结尾删除,最长匹配
file="archive.tar.gz"
echo "${file%%.*}" # 输出: archive(删除所有扩展名)
# 获取目录名
path="/home/user/docs/file.txt"
echo "${path%%/*}" # 输出: (空) 因为第一个字符就是/
echo "${path#/}%%/*" # 需要组合使用

三、实际应用场景
3.1 文件路径处理
#!/bin/bash
# file_ops.sh
file="/home/user/docs/report-2024.pdf"
# 提取文件名(去掉路径)
filename="${file##*/}"
echo "文件名: $filename" # report-2024.pdf
# 提取目录(去掉文件名)
dirname="${file%/*}"
echo "目录: $dirname" # /home/user/docs
# 提取文件名(去掉扩展名)
basename="${filename%.*}"
echo "基础名: $basename" # report-2024
# 提取扩展名
extension="${filename##*.}"
echo "扩展名: $extension" # pdf
# 提取年份
year="${basename##*-}"
echo "年份: $year" # 2024
3.2 URL处理
#!/bin/bash
url="https://example.com:8080/path/to/page?name=test"
# 提取协议
protocol="${url%%://*}"
echo "协议: $protocol" # https
# 提取域名和端口(去掉协议)
without_proto="${url#*://}"
domain_port="${without_proto%%/*}"
echo "域名+端口: $domain_port" # example.com:8080
# 提取域名(去掉端口)
domain="${domain_port%:*}"
echo "域名: $domain" # example.com
# 提取端口
port="${domain_port#*:}"
echo "端口: $port" # 8080
# 提取路径
path="${without_proto#*/}"
path="${path%%\?*}"
echo "路径: $path" # path/to/page
# 提取查询参数
query="${url#*\?}"
echo "查询: $query" # name=test
3.3 版本号处理
#!/bin/bash
version="1.2.3-beta"
# 提取主版本
major="${version%%.*}"
echo "主版本: $major" # 1
# 提取次版本
minor="${version#*.}"
minor="${minor%%.*}"
echo "次版本: $minor" # 2
# 提取修订号
patch="${version##*.}"
patch="${patch%%-*}"
echo "修订号: $patch" # 3
# 提取预发布标签
tag="${version#*-}"
echo "标签: $tag" # beta
四、组合使用
#!/bin/bash
# 复杂字符串处理
str="Hello-World-2024.txt"
# 1. 获取第一个单词
first="${str%%-*}"
echo "第一个: $first" # Hello
# 2. 获取最后一个部分
last="${str##*-}"
echo "最后一个: $last" # 2024.txt
# 3. 获取中间部分
middle="${str#*-}"
middle="${middle%-*}"
echo "中间: $middle" # World
# 4. 多步处理
echo "原始: $str"
echo "长度: ${#str}"
echo "前5字符: ${str:0:5}"
echo "后3字符: ${str: -3}"
echo "去掉后缀: ${str%.*}"
echo "去掉前缀: ${str#*-}"
五、总结
| 操作符 | 方向 | 匹配策略 | 记忆口诀 |
|---|---|---|---|
${#var} | – | 长度 | # 像数字符号 |
${var#pattern} | 开头 | 最短 | 一个#最小 |
${var##pattern} | 开头 | 最长 | 两个#最大 |
${var%pattern} | 结尾 | 最短 | 一个%最小 |
${var%%pattern} | 结尾 | 最长 | 两个%最大 |
规则很简单:
#从开头删,%从结尾删- 一个符号 删最短,两个符号 删最长
发表回复