高级 Bash 脚本编程指南

深入探索 shell 脚本编程艺术

Mendel Cooper

10

2014 年 3 月 10 日

修订历史
修订版 6.52012 年 4 月 5 日修订者:mc
'TUNGSTENBERRY' 版本
修订版 6.62012 年 11 月 27 日修订者:mc
'YTTERBIUMBERRY' 版本
修订版 102014 年 3 月 10 日修订者:mc
'PUBLICDOMAIN' 版本

本教程假设读者不具备任何脚本或编程的先验知识,但会快速进阶到中级/高级的指导水平... 同时偷偷地融入少许 UNIX® 的智慧和传说。 它可以作为教科书、自学手册以及 shell 脚本技术的参考和知识来源。 练习和带有大量注释的示例鼓励读者积极参与,其前提是真正学习脚本编写的唯一方法是编写脚本.

本书适合在课堂上用作编程概念的通用入门教材。

本文档在此授予公共领域。无版权!


献词

献给 Anita,所有魔法的源泉

目录
第一部分. 导言
1. Shell 编程!
2. 从 Sha-Bang 开始
第二部分. 基础知识
3. 特殊字符
4. 变量和参数介绍
5. 引用
6. 退出和退出状态
7. 测试
8. 运算和相关主题
第三部分. 超越基础
9. 变量的另一面
10. 操作变量
11. 循环和分支
12. 命令替换
13. 算术扩展
14. 休息时间
第四部分. 命令
15. 内部命令和内建命令
16. 外部过滤器、程序和命令
17. 系统和管理命令
第五部分. 高级主题
18. 正则表达式
19. Here Document
20. I/O 重定向
21. 子 Shell
22. 受限 Shell
23. 进程替换
24. 函数
25. 别名
26. 列表结构
27. 数组
28. 间接引用
29. /dev/proc
30. 网络编程
31. 关于零和空值
32. 调试
33. 选项
34. 陷阱
35. 脚本编写风格
36. 杂项
37. Bash,版本 2、3 和 4
38. 尾注
38.1. 作者注
38.2. 关于作者
38.3. 哪里可以获得帮助
38.4. 用于制作本书的工具
38.5. 致谢
38.6. 免责声明
参考书目
A. 贡献的脚本
B. 参考卡片
C. Sed 和 Awk 微型入门
C.1. Sed
C.2. Awk
D. 解析和管理路径名
E. 具有特殊含义的退出码
F. I/O 和 I/O 重定向详解
G. 命令行选项
G.1. 标准命令行选项
G.2. Bash 命令行选项
H. 重要文件
I. 重要系统目录
J. 可编程补全简介
K. 本地化
L. 历史命令
M. 示例 .bashrc.bash_profile 文件
N. 将 DOS 批处理文件转换为 Shell 脚本
O. 练习
O.1. 分析脚本
O.2. 编写脚本
P. 修订历史
Q. 下载和镜像站点
R. 待办事项列表
S. 版权
T. ASCII 表
索引
表列表
8-1. 运算符优先级
15-1. 作业标识符
33-1. Bash 选项
36-1. 转义序列中表示颜色的数字
B-1. 特殊 Shell 变量
B-2. TEST 运算符:二元比较
B-3. TEST 运算符:文件
B-4. 参数替换和扩展
B-5. 字符串操作
B-6. 杂项结构
C-1. 基本 sed 运算符
C-2. sed 运算符示例
E-1. 保留的 退出码
N-1. 批处理文件关键字/变量/运算符及其 Shell 等效项
N-2. DOS 命令及其 UNIX 等效项
P-1. 修订历史
示例列表
2-1. cleanup: 用于清理 /var/log 中日志文件的脚本
2-2. cleanup: 改进的清理脚本
2-3. cleanup: 上述脚本的增强和通用版本。
3-1. 代码块和 I/O 重定向
3-2. 将代码块的输出保存到文件
3-3. 在后台运行循环
3-4. 备份最近一天内更改的所有文件
4-1. 变量赋值和替换
4-2. 普通变量赋值
4-3. 变量赋值,普通和花哨
4-4. 整数还是字符串?
4-5. 位置参数
4-6. wh, whois 域名查找
4-7. 使用 shift
5-1. 回显奇怪的变量
5-2. 转义字符
5-3. 检测按键
6-1. exit / 退出状态
6-2. 使用 ! 取反条件
7-1. 什么是真?
7-2. test, /usr/bin/test, [ ], 和 /usr/bin/[ 的等效性
7-3. 使用 (( )) 进行算术测试
7-4. 测试断开的链接
7-5. 算术和字符串比较
7-6. 测试字符串是否为
7-7. zmore
8-1. 最大公约数
8-2. 使用算术运算
8-3. 使用 && 和 || 的复合条件测试
8-4. 数值常量的表示
8-5. C 风格的变量操作
9-1. $IFS 和空格
9-2. 定时输入
9-3. 再次,定时输入
9-4. 定时的 read
9-5. 我是 root 吗?
9-6. arglist: 使用 $* 和 $@ 列出参数
9-7. $*$@ 行为不一致
9-8. $IFS 为空时 $*$@
9-9. 下划线变量
9-10. 使用 declare 对变量进行类型声明
9-11. 生成随机数
9-12. 从一副牌中随机抽取一张牌
9-13. 布朗运动模拟
9-14. 值之间的随机数
9-15. 使用 RANDOM 掷骰子
9-16. 重新播种 RANDOM
9-17. 伪随机数,使用 awk
10-1. 在文本文件中段落之间插入空行
10-2. 生成 8 字符的“随机”字符串
10-3. 转换图形文件格式,并更改文件名
10-4. 将流音频文件转换为 ogg
10-5. 模拟 getopt
10-6. 提取和定位子字符串的替代方法
10-7. 使用参数替换和错误消息
10-8. 参数替换和“用法”消息
10-9. 变量的长度
10-10. 参数替换中的模式匹配
10-11. 重命名文件扩展名:
10-12. 使用模式匹配解析任意字符串
10-13. 匹配字符串前缀或后缀的模式
11-1. 简单的 for 循环
11-2. 每个 [列表] 元素中有两个参数的 for 循环
11-3. Fileinfo: 对变量中包含的文件列表进行操作
11-4. 对参数化的文件列表进行操作
11-5. 使用 for 循环对文件进行操作
11-6. for 循环中缺少 in [list]
11-7. 使用命令替换在 for 循环中生成 [list]
11-8. 用于二进制文件的 grep 替代品
11-9. 列出系统上的所有用户
11-10. 检查目录中所有二进制文件的作者
11-11. 列出目录中的符号链接
11-12. 目录中的符号链接,保存到文件
11-13. C 风格的 for 循环
11-14. 在批处理模式下使用 efax
11-15. 简单的 while 循环
11-16. 另一个 while 循环
11-17. 具有多个条件的 while 循环
11-18. while 循环中使用 C 风格的语法
11-19. until 循环
11-20. 嵌套循环
11-21. breakcontinue 在循环中的效果
11-22. 跳出多个循环级别
11-23. 在更高的循环级别继续
11-24. 在实际任务中使用 continue N
11-25. 使用 case
11-26. 使用 case 创建菜单
11-27. 使用命令替换生成 case 变量
11-28. 简单的字符串匹配
11-29. 检查字母输入
11-30. 使用 select 创建菜单
11-31. 在函数中使用 select 创建菜单
12-1. 愚蠢的脚本技巧
12-2. 从循环生成变量
12-3. 查找字谜
15-1. 一个脚本,它衍生自身的多个实例
15-2. printf 的实际应用
15-3. 变量赋值,使用 read
15-4. read 没有变量时会发生什么
15-5. 多行输入到 read
15-6. 检测方向键
15-7. read文件重定向 一起使用
15-8. 从管道读取时遇到的问题
15-9. 更改当前工作目录
15-10. let 进行算术运算。
15-11. 展示 eval 的效果
15-12. 使用 eval 在变量之间进行选择
15-13. 回显 命令行参数
15-14. 强制注销
15-15. rot13 的一个版本
15-16. set 与位置参数一起使用
15-17. 反转位置参数
15-18. 重新分配位置参数
15-19. “取消设置”变量
15-20. 使用 export 将变量传递给嵌入的 awk 脚本
15-21. 使用 getopts 读取传递给脚本的选项/参数
15-22. “包含”数据文件
15-23. 一个(无用的)脚本,它引用自身
15-24. exec 的效果
15-25. 一个 exec 自身的脚本
15-26. 等待进程完成再继续
15-27. 一个杀死自身的脚本
16-1. 使用 ls 为刻录 CDR 光盘创建目录
16-2. 你好还是再见
16-3. Badname, 删除当前目录中包含错误字符和空格的文件名。
16-4. inode 编号删除文件
16-5. Logfile: 使用 xargs 监控系统日志
16-6. 将当前目录中的文件复制到另一个目录
16-7. 按名称杀死进程
16-8. 使用 xargs 进行词频分析
16-9. 使用 expr
16-10. 使用 date
16-11. Date 计算
16-12. 词频分析
16-13. 哪些文件是脚本?
16-14. 生成 10 位随机数
16-15. 使用 tail 监控系统日志
16-16. 打印存储的电子邮件消息中的 From
16-17. 在脚本中模拟 grep
16-18. 纵横字谜求解器
16-19. 在韦氏 1913 年词典中查找定义
16-20. 检查列表中的单词是否有效
16-21. toupper: 将文件转换为全部大写。
16-22. lowercase: 将工作目录中的所有文件名更改为小写。
16-23. du: DOS 到 UNIX 文本文件转换。
16-24. rot13: 超弱加密。
16-25. 生成“密码引文”谜题
16-26. 格式化的文件列表。
16-27. 使用 column 格式化目录列表
16-28. nl: 自编号脚本。
16-29. manview: 查看格式化的手册页
16-30. 使用 cpio 移动目录树
16-31. 解包 rpm 归档文件
16-32. 从 C 程序文件中删除注释
16-33. 探索 /usr/X11R6/bin
16-34. 一个“改进的” strings 命令
16-35. 使用 cmp 比较脚本中的两个文件。
16-36. basenamedirname
16-37. 一个分段复制自身的脚本
16-38. 检查文件完整性
16-39. Uudecoding 编码文件
16-40. 找出在哪里报告垃圾邮件发送者
16-41. 分析垃圾邮件域
16-42. 获取股票报价
16-43. 更新 FC4
16-44. 使用 ssh
16-45. 一个给自己发送邮件的脚本
16-46. 生成素数
16-47. 抵押贷款的每月付款
16-48. 进制转换
16-49. 使用 here document 调用 bc
16-50. 计算 PI
16-51. 将十进制数转换为十六进制
16-52. 因式分解
16-53. 计算三角形的斜边
16-54. 使用 seq 生成循环参数
16-55. 字母计数”
16-56. 使用 getopt 解析命令行选项
16-57. 一个复制自身的脚本
16-58. 练习 dd
16-59. 捕获击键
16-60. Raspberry Pi 准备可引导的 SD 卡
16-61. 安全删除文件
16-62. 文件名生成器
16-63. 将米转换为英里
16-64. 使用 m4
17-1. 设置新密码
17-2. 设置 擦除 字符
17-3. 秘密密码: 关闭终端回显
17-4. 按键检测
17-5. 检查远程服务器的 identd
17-6. pidof 帮助杀死进程
17-7. 检查 CD 镜像
17-8. 在文件中创建文件系统
17-9. 添加新硬盘驱动器
17-10. 使用 umask 从窥探的眼睛中隐藏输出文件
17-11. Backlight: 更改(笔记本电脑)屏幕背光的亮度
17-12. killall, 来自 /etc/rc.d/init.d
19-1. broadcast: 向所有登录用户发送消息
19-2. dummyfile: 创建一个 2 行的虚拟文件
19-3. 使用 cat 的多行消息
19-4. 带有制表符抑制的多行消息
19-5. 带有可替换参数的 Here Document
19-6. 将文件对上传到 Sunsite incoming 目录
19-7. 参数替换已关闭
19-8. 一个生成另一个脚本的脚本
19-9. Here Document 和函数
19-10. “匿名” Here Document
19-11. 注释掉代码块
19-12. 自文档化脚本
19-13. 将行添加到文件开头
19-14. 解析邮箱
20-1. 使用 exec 重定向 stdin
20-2. 使用 exec 重定向 stdout
20-3. 在同一脚本中使用 exec 重定向 stdinstdout
20-4. 避免子 Shell
20-5. 重定向的 while 循环
20-6. 重定向的 while 循环的替代形式
20-7. 重定向的 until 循环
20-8. 重定向的 for 循环
20-9. 重定向的 for 循环(stdinstdout 都被重定向)
20-10. 重定向的 if/then 测试
20-11. 用于上述示例的数据文件 names.data
20-12. 记录事件
21-1. 子 Shell 中的变量作用域
21-2. 列出用户配置文件
21-3. 在子 Shell 中运行并行进程
22-1. 在受限模式下运行脚本
23-1. 不 fork 的代码块重定向
23-2. 进程替换 的输出重定向到循环中。
24-1. 简单函数
24-2. 带参数的函数
24-3. 传递给脚本的函数和命令行参数
24-4. 将间接引用传递给函数
24-5. 解引用传递给函数的参数
24-6. 再次,解引用传递给函数的参数
24-7. 两个数字的最大值
24-8. 将数字转换为罗马数字
24-9. 测试函数中的大返回值
24-10. 比较两个大整数
24-11. 从用户名获取真实姓名
24-12. 局部变量可见性
24-13. 简单递归函数演示
24-14. 另一个简单的演示
24-15. 递归,使用局部变量
24-16. 斐波那契数列
24-17. 汉诺塔
25-1. 脚本中的别名
25-2. unalias: 设置和取消设置别名
26-1. 使用 and list 测试命令行参数
26-2. 使用 and list 的另一个命令行参数测试
26-3. or listsand list 结合使用
27-1. 简单数组用法
27-2. 格式化诗歌
27-3. 各种数组操作
27-4. 对数组进行字符串操作
27-5. 将脚本内容加载到数组中
27-6. 数组的一些特殊属性
27-7. 关于空数组和空元素
27-8. 初始化数组
27-9. 复制和连接数组
27-10. 更多关于连接数组
27-11. 冒泡排序
27-12. 嵌入数组和间接引用
27-13. 埃拉托斯特尼筛法
27-14. 埃拉托斯特尼筛法,优化版
27-15. 模拟下推栈
27-16. 复杂的数组应用:探索奇怪的数学序列
27-17. 模拟二维数组,然后倾斜它
28-1. 间接变量引用
28-2. 将间接引用传递给 awk
29-1. 使用 /dev/tcp 进行故障排除
29-2. 播放音乐
29-3. 查找与 PID 关联的进程
29-4. 在线连接状态
30-1. 打印服务器环境
30-2. IP 地址
31-1. 隐藏 cookie 罐
31-2. 使用 /dev/zero 设置交换文件
31-3. 创建 ramdisk
32-1. 有 bug 的脚本
32-2. 缺少关键字
32-3. test24: 另一个有 bug 的脚本
32-4. 使用 assert 测试条件
32-5. 在退出时捕获
32-6. Control-C 后清理
32-7. 进度条的简单实现
32-8. 跟踪变量
32-9. 运行多个进程(在 SMP 机器上)
34-1. 数值和字符串比较不等价
34-2. 子 Shell 陷阱
34-3. echo 的输出通过管道传递给 read
36-1. shell wrapper
36-2. 稍微复杂的 shell wrapper
36-3. 一个通用的 shell wrapper,它写入日志文件
36-4. 围绕 awk 脚本的 shell wrapper
36-5. 围绕另一个 awk 脚本的 shell wrapper
36-6. 嵌入在 Bash 脚本中的 Perl
36-7. Bash 和 Perl 脚本的组合
36-8. 嵌入在 Bash 脚本中的 Python
36-9. 一个会说话的脚本
36-10. 一个(无用的)递归调用自身的脚本
36-11. 一个(有用)的递归调用自身的脚本
36-12. 另一个(有用)的递归调用自身的脚本
36-13. 一个 "彩色化" 的地址数据库
36-14. 绘制一个方框
36-15. 回显彩色文本
36-16. 一个 "赛马" 游戏
36-17. 进度条
36-18. 返回值技巧
36-19. 更多返回值技巧
36-20. 传递和返回数组
36-21. 字谜的乐趣
36-22. 从 shell 脚本调用的窗口小部件
36-23. 测试套件
37-1. 字符串扩展
37-2. 间接变量引用 - 新方法
37-3. 简单的数据库应用,使用间接变量引用
37-4. 使用数组和其他杂项技巧从一副牌中分发四手随机牌
37-5. 一个简单的地址数据库
37-6. 一个稍微更精细的地址数据库
37-7. 测试字符
37-8. 读取 N 个字符
37-9. 使用 here document 设置变量
37-10. 将输入通过管道传递给 read
37-11. 负数组索引
37-12. 字符串提取构造中的负参数
A-1. mailformat: 格式化电子邮件消息
A-2. rn: 一个简单的文件重命名工具
A-3. blank-rename: 重命名包含空格的文件名
A-4. encryptedpw: 使用本地加密密码上传到 ftp 站点
A-5. copy-cd: 复制数据 CD
A-6. 考拉兹序列
A-7. days-between: 两个日期之间的天数
A-8. 制作 dictionary
A-9. Soundex 转换
A-10. Game of Life
A-11. 用于 Game of Life 的数据文件
A-12. behead: 移除邮件和新闻消息头
A-13. password: 生成随机 8 字符密码
A-14. fifo: 使用命名管道进行每日备份
A-15. 使用模运算符生成素数
A-16. tree: 显示目录树
A-17. tree2: 备用目录树脚本
A-18. string functions: C 风格字符串函数
A-19. 目录信息
A-20. 哈希函数库
A-21. 使用哈希函数着色文本
A-22. 更多关于哈希函数
A-23. 挂载 USB 钥匙链存储设备
A-24. 转换为 HTML
A-25. 保存网络日志
A-26. 保护文字字符串
A-27. 取消保护文字字符串
A-28. 垃圾邮件发送者识别
A-29. 垃圾邮件发送者追捕
A-30. 使 wget 更易于使用
A-31. 一个 podcasting 脚本
A-32. 每晚备份到 FireWire 硬盘
A-33. 一个扩展的 cd 命令
A-34. 声卡设置脚本
A-35. 在文本文件中定位分割的段落
A-36. 插入排序
A-37. 标准差
A-38. 一个为共享软件作者准备的 pad 文件生成器
A-39. 一个 man page 编辑器
A-40. 玫瑰花瓣
A-41. Quacky:一种 Perquackey 型文字游戏
A-42. 尼姆游戏
A-43. 一个命令行秒表
A-44. 一个通用的 shell 脚本家庭作业解决方案
A-45. 骑士巡逻
A-46. 幻方
A-47. 十五拼图
A-48. 汉诺塔,图形版本
A-49. 汉诺塔,备用图形版本
A-50. getopt-simple.sh 脚本的备用版本
A-51. Tab Expansion 附录中使用的 UseGetOpt.sh 示例版本
A-52. 循环显示所有可能的颜色背景
A-53. 摩尔斯电码练习
A-54. Base64 编码/解码
A-55. 使用 sed 在文件中插入文本
A-56. 格龙斯费尔德密码
A-57. 宾果数字生成器
A-58. 基础知识回顾
A-59. 测试各种命令的执行时间
A-60. 关联数组与传统数组(执行时间)
C-1. 统计字母出现次数
J-1. UseGetOpt.sh 的补全脚本
M-1. 示例 .bashrc 文件
M-2. .bash_profile 文件
N-1. VIEWDATA.BAT:DOS 批处理文件
N-2. viewdata.sh:VIEWDATA.BAT 的 Shell 脚本转换
T-1. 一个生成 ASCII 表的脚本
T-2. 另一个 ASCII 表脚本
T-3. 第三个 ASCII 表脚本,使用 awk