34·Shell 基础进阶

字符串操作与参数展开

stringsexpansion

第34课:字符串操作与参数展开

所属模块: Shell 基础 难度: Intermediate 标签: stringsexpansion 上一课: 第33课 - 环境变量与 export 下一课: 第35课 - if 条件判断


1. 学习目标

完成本课学习后,你将能够:

  • 掌握 Bash 中常用的字符串操作,如获取长度、截取子串。
  • 熟练使用参数展开(Parameter Expansion)进行变量值的查找、替换、删除等高级操作。
  • 理解并运用模式匹配(Pattern Matching)来增强字符串处理能力。
  • 能够组合使用这些技术,编写更强大、更简洁的 Shell 脚本来处理文本和文件名。

2. 核心概念

在 Shell 编程中,变量和字符串是数据处理的核心。除了简单的赋值和引用,Bash 提供了一系列强大的“工具箱”来处理变量内容,这就是参数展开。它允许我们在变量被引用时,对其值进行各种变换,而无需启动外部程序(如 grep, sed, awk),效率更高,代码也更简洁。

2.1 字符串长度与截取

  • 获取长度: ${#variable}。这会返回变量 variable 中字符的个数。
  • 截取子串: ${variable:offset:length}。从变量值中,从 offset(偏移量,从0开始计数)位置起,提取 length 个字符。length 可省略,表示提取到末尾。
    • 类比: 想象字符串是一根绳子,${var:5:3} 就是从绳子第6个结(偏移量5)开始,量出3个结的长度剪下来。

2.2 参数展开之查找与替换

这是一组非常强大的操作,语法类似文字处理软件中的“查找替换”,但功能更灵活。

  • 查找并替换: ${variable/pattern/replacement}
    • 只替换第一个匹配 pattern 的部分。
    • ${variable//pattern/replacement}:替换所有匹配的部分。
    • ${variable/#pattern/replacement}:如果 pattern 匹配变量值的开头,则替换。
    • ${variable/%pattern/replacement}:如果 pattern 匹配变量值的末尾,则替换。
    • 注意: pattern 支持通配符(*, ?, [...])。

2.3 参数展开之删除

使用 #, % 符号来删除变量值的一部分。

  • 从开头删除(最短匹配): ${variable#pattern}
    • 删除变量值开头与 pattern 匹配的最短部分。
  • 从开头删除(最长匹配): ${variable##pattern}
    • 删除变量值开头与 pattern 匹配的最长部分。
  • 从末尾删除(最短匹配): ${variable%pattern}
    • 删除变量值末尾与 pattern 匹配的最短部分。
  • 从末尾删除(最长匹配): ${variable%%pattern}
    • 删除变量值末尾与 pattern 匹配的最长部分。
    • 记忆口诀: # 号像键盘上的 Home 键,处理开头% 号像键盘上的 End 键,处理末尾。单符号(#, %)是最短匹配(保守删除),双符号(##, %%)是最长匹配(贪婪删除)。

3. 代码示例

以下示例在 Bash Shell 中可直接运行,请仔细阅读注释。

#!/bin/bash

# === 基础字符串操作 ===
str="Hello, World! This is a string."

echo "原始字符串: '$str'"
echo "字符串长度: ${#str}"

# 截取子串:从偏移量7开始(第一个字符偏移量为0),截取5个字符
echo "截取 'World': ${str:7:5}"

# 截取子串:从偏移量13开始,截取到末尾
echo "截取后半部分: ${str:13}"

# === 参数展开:查找与替换 ===
my_path="/home/user/documents/report.txt"

echo "原始路径: '$my_path'"

# 替换第一个出现的 'user' 为 'admin'
echo "替换第一个user: ${my_path/user/admin}"

# 替换所有出现的 '/' 为 '|' (注意转义反斜杠)
echo "替换所有斜杠: ${my_path//\//|}"

# 仅当字符串以 '/home' 开头时,替换为 '/data'
echo "替换开头: ${my_path/#/home//data}"

# 仅当字符串以 '.txt' 结尾时,替换为 '.log'
echo "替换后缀: ${my_path/%.txt/.log}"

# === 参数展开:删除操作(常用于处理文件路径和扩展名)===
file_name="archive.backup.tar.gz"
echo "文件名: '$file_name'"

# 从末尾删除最短匹配的 '.gz' (获取去掉最后.gz的文件名)
echo "去掉 .gz: ${file_name%.*}"

# 从末尾删除最长匹配的 '.*' (获取去掉所有扩展名的文件名)
echo "去掉所有扩展名: ${file_name%%.*}"

# 从开头删除最短匹配的 '*.' (获取最后一个扩展名)
echo "获取最后扩展名: ${file_name##*.}"

# 实用场景:获取脚本自身所在目录(不带脚本名)
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "当前脚本目录: '$script_dir'"

# 实用场景:安全地设置变量默认值
unset unset_var
echo "变量未设置时使用默认值: '${unset_var:-Default Value}'"

4. 实践练习

练习 1:基础截取 给定一个字符串 quote="The quick brown fox jumps over the lazy dog"

  1. 编写一行代码,打印出该字符串的长度。
  2. 编写一行代码,截取出单词 "brown"
  3. 编写一行代码,截取出从 "jumps" 开始到字符串结尾的内容。

练习 2:文件名处理 假设有一个变量 file="my_script.backup.sh"

  1. 使用参数展开,提取出不带任何扩展名的基文件名(即 my_script)。
  2. 使用参数展开,将文件名中的第一个 .backup 部分删除(结果应为 my_script.sh)。

练习 3:模拟日志路径转换(综合应用) 你有一些旧的服务器日志路径,格式为 /var/log/oldapp/access-2023-10-01.log.gz。 你需要将它们转换为新的存储路径格式:/data/logs/newapp/2023-10-01/access.log。 请使用参数展开完成以下转换(假设变量 old_path 已存储旧路径):

  1. 提取日期部分(如 2023-10-01)。
  2. 构造新路径。 你可以先尝试为 old_path="/var/log/oldapp/access-2023-10-01.log.gz" 编写转换代码。

5. 常见错误

  1. 偏移量计数错误: 记住偏移量从 0 开始,而不是 1。${var:1} 提取的是从第二个字符开始的内容。
  2. 混淆 #, %##, %%
    • 对于字符串 file.tar.gz
    • ${file%.*} 结果是 file.tar(最短匹配,删除末尾的 .* 即最后一个 .gz)。
    • ${file%%.*} 结果是 file(最长匹配,删除末尾的 .*.tar.gz)。
    • 需要根据你想要保留还是删除的部分来谨慎选择。
  3. 忘记双引号: 在模式匹配和替换中,如果变量包含空格或特殊字符,用双引号将变量括起来是更安全的做法,例如 "${var// /_}"。但对于 #, % 删除操作,模式中的通配符即使加了引号也可能被解释,具体行为取决于 Shell 选项,通常直接使用即可。
  4. 贪婪匹配的意外结果: 使用 ##%% 进行贪婪匹配时要格外小心,它可能会删除比你预期多得多的内容。先在小例子上测试。

6. 小结

本节课我们深入学习了 Bash 中非常实用的字符串处理技巧:

  • 基础操作: 使用 ${#var} 获取长度, ${var:offset:length} 截取子串。
  • 参数展开的精髓: 允许我们在引用变量的同时对其值进行“二次加工”。
    • 替换: ${var/pattern/repl} 及其变体。
    • 删除: ${var#pattern}, ${var##pattern}, ${var%pattern}, ${var%%pattern}。记住 # 管开头,% 管结尾。
  • 核心价值: 这些操作是 Shell 内建的,执行速度快,是编写高效、简洁运维脚本(如批量文件重命名、日志分析、路径构造)的必备技能。

熟练掌握字符串操作与参数展开,将极大提升你编写 Shell 脚本处理文本和数据的灵活性和能力。

练习编辑器

rust
Loading...

继续学习

完成本课后,建议继续学习下一课「if 条件判断」 以巩固所学知识。