人人范文网 范文大全

高级Bash脚本编程指南

发布时间:2020-03-03 19:28:43 来源:范文大全 收藏本文 下载本文 手机版

译者序

毫无疑问,UNIX/Linux 最重要的软件之一就是 shell,目前最流行的 shell 被称为 Bash(Bourne Again Shell),几乎所有的 Linux 和绝大部分的 UNIX 都可以使用 Bash。作为系统与用户之间的交互接口,shell 几乎是你在 UNIX 工作平台上最亲密的朋友,因此,学好 shell,是学习Linux/UNIX 的的开始,并且它 会始终伴随你的工作学习。

shell 是如此地重要,但令人惊奇的是,介绍 shell 的书没有真正令人满意的。所幸的是,我看到了 这本被人称为 abs 的书,这本书介绍了 bash 大量的细节和广阔的范围,我遇到的绝大部分的技术问题 --无论是我忘记的或是以前没有发现的--都可以在这本书里找到答案。这本使用大量的例子详细地介绍 了 Bash 的语法,各种技巧,调试等等的技术,以循序渐进的学习方式,让你了解 Bash 的所有特性, 在书中还有许多练习可以引导你思考,以得到更深入的知识。无论你是新手还是老手,或是使用其他 语言的程序员,我能肯定你能在此书用受益。而本书除了介绍 BASH 的知识之外,也有许多有用的关 于 Linux/UNIX 的知识和其他 shell 的介绍。

在看到本书的英文版后,我决定把它翻译出来,在 Linuxsir 论坛上结识了译者之一杨春敏共同翻

译这本书,600 多页的书是本大部头的书,我们花了 6 个月的业余时间才翻译完了。

关于版权的问题,英文版的作者 Mendel Cooper 对英文版的版权做了详细的约定,请参考: Appendix Q.Copyright。中文版版权由译者杨春敏和黄毅共同所有,在遵守英文版版权相应条款的条 件下,欢迎在保留本书译者名字和版权说明以非盈利的方式自由发布此中文版,以盈利目的的所有行 为必须联系英文作者和两位中文译者以获得许可。

本书得以成稿,我(黄毅)要多谢我的女朋友,本该给予她的时间我用来了翻译,多谢你的理解, 你是一个很棒的女朋友!

译者 杨春敏 黄毅 2006.5.15

Advanced Bash-Scripting Guide > 一本深入学习shell 脚本艺术的书籍

Version 3.7.2

2005/11/16

作者:Mendel Cooper

mail:thegrendel@theriver.com

这本书假定你没有任何脚本或一般程序的编程知识,但是如果你有相关的知识,那么你将很容易 达到中高级的水平...all the while sneaking in little snippets of UNIX? wisdom and lore(这句不知道怎么译).你可以把本书作为教材,自学手册,或者你获得 shell 脚本技术的文档.书中的练习和例子脚本中的注释将会与读者有更好的互动,但是最关键的前提是: 想真正学习脚本编程的唯一途径就是编写脚本.

这本书也可作为教材来讲解一般的编程概念.

下载本书最新版本,http://personal.riverusers.com/~thegrendel/abs-guide-3.7.tar.bz2, 这是一个以 tar 和 bzip2 进行打包的,并且是以 HTML 来发行的.当然,你也可以获得本书的 pdf 版本 在 http://www.daodoc.com/LDP/abs/abs-guide.pdf.可以在

http://personal.riverusers.com/~thegrendel/Change.log 中查看修订历史.

译者:杨春敏,黄毅

mail:chunmin.yang@gmail.com

一直想好好学习一下 bash,可惜网上的资料都杂乱不堪,我还是喜欢通过一本书系统的学习.这本 书来得正是时候.本书的作者真是非常的严谨,从例子里的改进人名单就能看出来.可惜我水平真 的是非常有限,好多地方估计译得都有问题.希望阅读的朋友们多多提些修改建议.我会尽我的最 大努力去修正它.

目录 ++++ 第一部分.热身

1. 为什么使用

2.1. 调用一个脚本

2.2. 初步的练习 shell 编程

2. 带着一个 Sha-Bang 出发(Sha-Bang 指的是#!) 第二部分.基本

3. 特殊字符

4. 变量和参数的介绍

4.1. 变量替换

4.2. 变量赋值

4.3.Bash 变量是不分类型的

4.4. 特殊的变量类型

5. 引用(翻译的可能有问题,特指引号)

5.1. 引用变量

5.2. 转义(\)

6. 退出和退出状态

7.Tests

7.1.Test 结构

7.2. 文件测试操作

7.3. 其他比较操作

7.4. 嵌套的

8. 操作符和相关的主题

8.1. 操作符

8.2. 数字常量

if/then 条件 test

7.5. 检查你的 test 知识

第三部分.超越基本

9. 变量重游

9.1. 内部变量

9.2. 操作字符串

9.3. 参数替换

9.4. 指定类型的变量:declare 或者

9.5. 变量的间接引用

9.6.$RANDOM: 产生随机整数

9.7. 双圆括号结构

10. 循环和分支

10.1. 循环

10.2. 嵌套循环

10.3. 循环控制

10.4. 测试与分支(case 和

11. 内部命令与内建

11.1. 作业控制命令

12. 外部过滤器,程序和命令

12.1. 基本命令

12.2. 复杂命令

12.3. 时间/日期

typeset

select 结构)

命令

12.4. 文本处理命令

12.5. 文件与归档命令

12.6. 通讯命令

12.7. 终端控制命令

12.8. 数学计算命令

12.9. 混杂命令

13. 系统与管理命令

13.1. 分析一个系统脚本

14. 命令替换

15. 算术扩展

16.I/O 重定向

16.1. 使用

16.3. 应用

exec

16.2. 代码块的重定向

17.Here Documents

17.1.Here Strings

18. 休息时间

Part 4. 高级

19. 正则表达式

19.1. 一个简要的正则表达式介绍

19.2. 通配

20. 子 shell(Subshells)

21. 受限 shell(Restricted Shells)

22. 进程替换

23. 函数

23.1. 复杂函数和函数复杂性

23.2. 局部变量

23.3. 不使用局部变量的递归

24. 别名(Aliases)

25. 列表结构

26. 数组

27./dev 和 /proc

27.1./dev

27.2./proc

28. 关于

29. 调试

30. 选项

31.Gotchas

32. 脚本编程风格

32.1. 非官方的

33. 杂项

33.1. 交互式和非交互式的

33.2.Shell 包装

33.3. 测试和比较: 另一种方法

33.4. 递归

33.5. 彩色脚本 Zeros 和 Nulls

Shell 脚本风格

shells 和脚本

33.6. 优化

33.7. 各种小技巧

33.8. 安全话题

33.8.1.被感染的脚本 33.8.2.隐藏 Shell 脚本源码

33.9. 移植话题

33.10.

34.1.Bash, 版本

34.2.Bash, 版本

Windows 下进行 Shell 编程

34.Bash, 版本 2 和 3

2 3 35.后记

35.1. 作者后记

35.2. 关于作者

35.3. 哪里可以取得帮助?

35.4. 制作这本书的工具

35.4.1. 硬件

35.4.2. 软件和排版软件

35.5.Credits

Bibliography

A.Contributed Scripts B.Reference Cards

C.A Sed and Awk Micro-Primer

C.1.Sed

C.2.Awk

D.Exit Codes With Special Meanings

E.A Detailed Introduction to I/O and I/O Redirection F.Standard Command-Line Options G.Important Files H.Important System Directories I.Localization J.History Commands K.A Sample .bashrc File

L.Converting DOS Batch Files to Shell Scripts

M.Exercises

M.1.Analyzing Scripts

M.2.Writing Scripts

N.Revision History O.Mirror Sites

P.To Do List Q.Copyright

表格清单:

11-1.作业标识符

30-1.Bash 选项

33-1.转义序列中数值和彩色的对应 B-1.Special Shell Variables B-2.TEST Operators: Binary Comparison B-3.TEST Operators: Files B-4.Parameter Substitution and Expansion B-5.String Operations B-6.Miscellaneous Constructs C-1.Basic sed operators

C-2.Examples of sed operators D-1."Reserved" Exit Codes L-1.Batch file keywords / variables / operators, and their shell equivalents L-2.DOS commands and their UNIX equivalents N-1.Revision History

例子清单:

2-1. 清除:清除/var/log 下的 log 文件 2-2. 清除:一个改良的清除脚本

2-3.cleanup:一个增强的和广义的删除 logfile 的脚本 3-1. 代码块和 I/O 重定向

3-2. 将一个代码块的结果保存到文件 3-3. 在后台运行一个循环

3-4. 备份最后一天所有修改的文件.4-1. 变量赋值和替换 4-2. 一般的变量赋值

4-3. 变量赋值,一般的和比较特殊的 4-4. 整型还是 string? 4-5. 位置参数

4-6.wh,whois 节点名字查询 4-7. 使用 shift

5-1.echo 一些诡异的变量 5-2. 转义符

6-1.exit/exit 状态

6-2. 否定一个条件使用! 7-1. 什么情况下为真? 7-2. 几个等效命令 test,/usr/bin/test,[],和/usr/bin/[ 7-3. 算数测试使用(( )) 7-4.test 死的链接文件 7-5. 数字和字符串比较

7-6. 测试字符串是否为 null 7-7.zmore 8-1. 最大公约数

8-2. 使用算术操作符

8-3. 使用&&和||进行混合状态的 test 8-4. 数字常量的处理 9-1.$IFS 和空白 9-2. 时间输入

9-3. 再来一个时间输入 9-4.Timed read 9-5. 我是 root?

9-6.arglist:通过$*和$@列出所有的参数 9-7. 不一致的$*和$@行为 9-8. 当$IFS 为空时的$*和$@ 9-9. 下划线变量

9-10.在一个文本文件的段间插入空行 9-11.利用修改文件名,来转换图片格式 9-12.模仿 getopt 命令

9-13.提取字符串的一种可选的方法 9-14.使用参数替换和 error meages 9-15.参数替换和"usage"meages 9-16.变量长度

9-17.参数替换中的模式匹配 9-18.重命名文件扩展名

9-19.使用模式匹配来分析比较特殊的字符串 9-20.对字符串的前缀或后缀使用匹配模式 9-21.使用 declare 来指定变量的类型 9-22.间接引用

9-23.传递一个间接引用给 awk 9-24.产生随机数

9-25.从一副扑克牌中取出一张随机的牌 9-26.两个指定值之间的随机数 9-27.使用随机数来摇一个骰子 9-28.重新分配随机数种子 9-29.使用 awk 产生伪随机数

9-30.C 风格的变量处理 10-1.循环的一个简单例子

10-2.每个[list]元素带两个参数的 for 循环

10-3.文件信息:对包含在变量中的文件列表进行操作 10-4. 在 for 循环中操作文件 10-5.在 for 循环中省略[list] 10-6.使用命令替换来产生 for 循环的[list] 10-7. 对于二进制文件的一个 grep 替换 10-8.列出系统上的所有用户

10-9.在目录的所有文件中查找源字串 10-10. 列出目录中所有的符号连接文件

10-11. 将目录中的符号连接文件名保存到一个文件中 10-12. 一个 C 风格的 for 循环 10-13. 在 batch mode 中使用 efax 10-14. 简单的 while 循环 10-15. 另一个 while 循环 10-16. 多条件的 while 循环 10-17.C 风格的 while 循环 10-18.until 循环 10-19. 嵌套循环

10-20.break 和 continue 命令在循环中的效果 10-21. 多层循环的退出

10-22. 多层循环的 continue 10-23. 在实际的任务中使用"continue N" 10-24. 使用 case 10-25. 使用 case 来创建菜单

10-26. 使用命令替换来产生 case 变量 10-27. 简单字符串匹配 10-28. 检查是否是字母输入 10-29. 用 select 来创建菜单

10-30. 用函数中 select 结构来创建菜单 11-1.一个 fork 出多个自己实例的脚本 11-2.printf 11-3.使用 read,变量分配

11-4. 当使用一个不带变量参数的 read 命令时,将会发生什么? 11-5.read 命令的多行输入 11-6.检测方向键

11-7. 通过文件重定向来使用 read 11-8.管道输出到 read 中的问题 11-9.修改当前的工作目录

11-10. 用"let"命令来作算术操作.11-11. 显示 eval 命令的效果 11-12. 强制登出(log-off) 11-13. 另一个"rot13"的版本

11-14. 在 Perl 脚本中使用 eval 命令来强制变量替换 11-15. 使用 set 来改变脚本的位置参数 11-16. 重新分配位置参数 11-17.Unset 一个变量

11-18. 使用 export 命令传递一个变量到一个内嵌 awk 的脚本中 11-19. 使用 getopts 命令来读取传递给脚本的选项/参数.11-20."Including"一个数据文件

11-21. 一个没什么用的,source 自身的脚本 11-22.exec 的效果

11-23. 一个 exec 自身的脚本

11-24. 在继续处理之前,等待一个进程的结束 11-25. 一个结束自身的脚本.12-1.使用 ls 命令来创建一个烧录 CDR 的内容列表

12-2.Hello or Good-bye

12-3.删除当前目录下文件名中包含一些特殊字符(包括空白)的文件..12-4. 通过文件的 inode 号来删除文件 12-5.Logfile: 使用 xargs 来监控系统 log 12-6.把当前目录下的文件拷贝到另一个文件中 12-7. 通过名字 Kill 进程

12-8.使用 xargs 分析单词出现的频率 12-9.使用 expr

12-10. 使用 date 命令

12-11. 分析单词出现的频率 12-12. 那个文件是脚本? 12-13. 产生 10 进制随机数

12-14. 使用 tail 命令来监控系统 log 12-15. 在一个脚本中模仿 "grep" 的行为 12-16. 在 1913 年的韦氏词典中查找定义 12-17. 检查列表中单词的正确性 12-18. 转换大写: 把一个文件的内容全部转换为大写.12-19. 转换小写: 将当前目录下的所有文全部转换为小写.12-20.Du: DOS 到 UNIX 文本文件的转换.12-21.rot13: rot13, 弱智加密.12-22.Generating "Crypto-Quote" Puzzles 12-23. 格式化文件列表.12-24. 使用 column 来格式化目录列表 12-25.nl: 一个自己计算行号的脚本.12-26.manview: 查看格式化的 man 页 12-27. 使用 cpio 来拷贝一个目录树 12-28. 解包一个 rpm 归档文件 12-29. 从 C 文件中去掉注释

12-30.Exploring /usr/X11R6/bin 12-31. 一个"改进过"的 strings 命令

12-32. 在一个脚本中使用 cmp 来比较 2 个文件.

12-33.basename 和 dirname 12-34. 检查文件完整性

12-35.Uudecod 编码后的文件

12-36. 查找滥用的连接来报告垃圾邮件发送者 12-37. 分析一个垃圾邮件域 12-38. 获得一份股票报价 12-39. 更新

Fedora Core 4

12-40. 使用 h 12-41. 一个可以 mail 自己的脚本 12-42. 按月偿还贷款 12-43. 数制转换

12-44. 使用 "here document" 来调用 bc 12-45. 计算圆周率

12-46. 将 10 进制数字转换为 16 进制数字 12-47. 因子分解

12-48. 计算直角三角形的斜边 12-49. 使用 seq 来产生循环参数 12-50. 字母统计

12-51. 使用 getopt 来分析命令行选项 12-52. 一个拷贝自身的脚本 12-53. 练习dd 12-54. 记录按键

12-55. 安全的删除一个文件 12-56. 文件名产生器 12-57. 将米转换为英里 12-58. 使用 m4 13-1.设置一个新密码 13-2.设置一个擦除字符

13-3.关掉终端对于密码的 echo 13-4. 按键检测

13-5.Checking a remote server for identd 13-6.pidof 帮助杀掉一个进程 13-7. 检查一个 CD 镜像

13-8.在一个文件中创建文件系统 13-9.添加一个新的硬盘驱动器

13-10. 使用 umask 来将输出文件隐藏起来 13-11.killall, 来自于 /etc/rc.d/init.d 14-1.愚蠢的脚本策略

14-2.从循环的输出中产生一个变量 14-3.找 anagram(回文构词法, 可以将一个有意义的单词, 个或多个有意义的单词, 但 是还是原来的子母集合) 16-1.使用 exec 重定向标准输入 16-2.使用 exec 来重定向 stdout 16-3.使用 exec 在同一脚本中重定向 stdin 和 stdout

变换为 1

16-4. 避免子 shell 16-5.while 循环的重定向

16-6.另一种 while 循环的重定向 16-7.until 循环重定向 16-8.for 循环重定向

16-9.for 循环重定向 loop (将标准输入和标准输出都重定向了) 16-10. 重定向 if/then 测试结构

16-11. 用于上面例子的"names.data"数据文件 16-12. 记录日志事件

17-1.广播: 发送消息给每个登录上的用户 17-2.仿造文件: 创建一个两行的仿造文件 17-3.使用 cat 的多行消息

17-4. 带有抑制 tab 功能的多行消息 17-5.使用参数替换的 here document

17-6.上传一个文件对到"Sunsite"的 incoming 目录 17-7. 关闭参数替换

17-8.一个产生另外一个脚本的脚本 17-9.Here documents 与函数 17-10."匿名" here Document 17-11. 注释掉一段代码块

17-12. 一个自文档化(self-documenting)的脚本 17-13. 在一个文件的开头添加文本 20-1.子 shell 中的变量作用域 20-2.列出用户的配置文件

20-3.在子 shell 里进行串行处理 21-1.在受限的情况下运行脚本 23-1.简单函数

23-2.带着参数的函数

23-3.函数和被传给脚本的命令行参数 23-4. 传递间接引用给函数

23-5.解除传递给函数的参数引用

23-6.再次尝试解除传递给函数的参数引用 23-7. 两个数中的最大者 23-8.把数字转化成罗马数字 23-9.测试函数最大的返回值 23-10. 比较两个大整数 23-11. 用户名的真实名 23-12. 局部变量的可见范围 23-13. 用局部变量来递归 23-14. 汉诺塔 24-1.脚本中的别名

24-2.unalias: 设置和删除别名

25-1.使用"与列表(and list)"来测试命令行参数 25-2.用"与列表"的另一个命令行参数测试

25-3."或列表"和"与列表"的结合使用 26-1.简单的数组用法 26-2.格式化一首诗 26-3.多种数组操作

26-4. 用于数组的字符串操作符 26-5.将脚本的内容传给数组 26-6.一些数组专用的工具

26-7. 关于空数组和空数组元素 26-8.初始化数组

26-9.复制和连接数组

26-10. 关于连接数组的更多信息 26-11. 一位老朋友: 冒泡排序 26-12. 内嵌数组和间接引用

26-13. 复杂数组应用: 埃拉托色尼素数筛子 26-14. 模拟下推的堆栈

26-15. 复杂的数组应用: 列出一种怪异的数学序列 26-16. 模拟二维数组,并使它倾斜 27-1.利用/dev/tcp 来检修故障 27-2.搜索与一个 PID 相关的进程 27-3.网络连接状态

28-1.隐藏 cookie 而不再使用

28-2.用/dev/zero 创建一个交换临时文件 28-3.创建 ramdisk 29-1.一个错误的脚本

29-2.丢失关键字(keyword) 29-3.另一个错误脚本

29-4. 用"aert"测试条件 29-5.捕捉 exit

29-6.在 Control-C 后清除垃圾 29-7. 跟踪变量

29-8.运行多进程

(在多处理器的机器里) 31-1.数字和字符串比较是不相等同的 31-2.子 SHELL 缺陷

31-3.把 echo 的输出用管道输送给 read 命令 33-1.shell 包装

33-2.稍微复杂一些的 shell 包装 33-3.写到日志文件的 shell 包装 33-4. 包装 awk 的脚本

33-5.另一个包装 awk 的脚本 33-6.把 Perl 嵌入 Bash 脚本 33-7.Bash 和 Perl 脚本联合使用 33-8.递归调用自己本身的(无用)脚本 33-9.递归调用自己本身的(有用)脚本

33-10. 另一个递归调用自己本身的(有用)脚本

33-11. 一个

"彩色的" 地址资料库 33-12. 画盒子

33-13. 显示彩色文本 33-14."赛马" 游戏 33-15. 返回值技巧

33-16. 整型还是 string? 33-17. 传递和返回数组 33-18.anagrams 游戏

33-19. 在 shell 脚本中调用的窗口部件 34-1.字符串扩展

34-2.间接变量引用.) | (cd /dest/directory && tar xpvf -)

2 # 从一个目录移动整个目录树到另一个目录

3 # [courtesy Alan Cox , with a minor change] 4

5 # 1) cd /source/directory

源目录

6 # 2) &&

与操作,如果

cd 命令成功了,那么就执行下边的命令

7 # 3) tar cf

'x'解档,'p'保证所有权和文件属性, 15 # 'v'发完整消息到 stdout 16 #

'f'后边跟'-',从 stdin 读取数据 17 # 18 #

注意:'x' 是一个命令, 'p', 'v', 'f' 19 # Whew! 20

21

22

23 # 更优雅的写法应该是 24 #

cd source/directory 25 #

tar cf2 # --未解压的 tar 文件--

| --然后把它传递到"tar"中-- 3 # 如果 "tar" 没能够正常的处理"bunzip2", 4 # 这就需要使用管道来执行 2 个单独的步骤来完成它.5 # 这个练习的目的是解档"bzipped"的 kernel 源文件.

####End Script

###### 注意:在上边这个例子中'-'不太象是 bash 的操作符,而更像是 tar 的参数.bash$echo "whatever" |catabc

standard input:

ASCII text

bash$file

最后,一个真实世界的使用 tar 命令的例子.

Example 3-4. 备份最后一天所有修改的文件.

####Start Script

#### 1 #!/bin/bash 2

3 # 在一个"tarball"中(经过 tar 和 gzip 处理过的文件) 4 #+ 备份最后 24 小时当前目录下 d 所有修改的文件. 5

6 BACKUPFILE=backup-$(date +%m-%d-%Y)

7 #

在备份文件中嵌入时间.

8 #

Thanks, Joshua Tschida, for the idea.

9 archive=${1:-$BACKUPFILE} 10 #

如果在命令行中没有指定备份文件的文件名, 11 #+ 那么将默认使用"backup-MM-DD-YYYY.tar.gz".12

13 tar cvf - `find .-mtime -1 -type f -print` > $archive.tar 14 gzip $archive.tar 15 echo "Directory $PWD backed up in archive file "$archive.tar.gz"."

16

17

18 #

Stephane Chazelas 指出上边代码, 19 #+ 如果在发现太多的文件的时候,或者是如果文件 20 #+ 名包括空格的时候,将执行失败.21

22 # Stephane Chazelas 建议使用下边的两种代码之一

23 # ----- 24 #

find .-mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar" 25 #

使用 gnu 版本的 find. 26

27

28 #

find .-mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \; 29 #

对于其他风格的

UNIX 便于移植,但是比较慢.30 # ----- 31

32

33 exit 0

####End Script

######

注意:以"-"开头的文件名在使用"-"作为重定向操作符的时候,可能会产生问题.应该写一个脚本来检查这个问题,并给这个文件加上合适的前缀.如: ./-FILENAME, $PWD/-FILENAME,或$PATHNAME/-FILENAME.

如果变量的值以"-"开头,可能也会引起问题.1 var="-n" 2 echo$var 3 #具有"echo -n"的效果了,这样什么都不会输出的.

-

之前工作的目录."cd -"将回到之前的工作目录,具体请参考"$OLDPWD"环境变量.注意:一定要和之前讨论的重定向功能分开,但是只能依赖上下文区分.

- 算术减号.

=

算术等号,有时也用来比较字符串.1 a=28

2 echo $a

# 28

+ + 算术加号,也用在正则表达式中.选项,对于特定的命令来说使用"+"来打开特定的选项,用"-"来关闭特定的选项.算术取模运算.也用在正则表达式中.%

~

home 目录.相当于$HOME 变量.~bozo 是 bozo 的 home 目录,并且 ls ~bozo 将列出其中

内容.~/就是当前用户的 home 目录,并且 ls ~/将列出其中的内容,如: bash$ echo ~bozo /home/bozo

bash$ echo ~ /home/bozo

bash$ echo ~/ /home/bozo/

bash$ echo ~: /home/bozo:

bash$ echo ~nonexistent-user ~nonexistent-user

~+

当前工作目录,相当于$PWD 变量.之前的工作目录,相当于$OLDPWD 内部变量.用于正则表达式,这个操作将在正则表达式匹配部分讲解,只有 version3 才支持.行首,正则表达式中表示行首."^"定位到行首.~-

=~

^

控制字符

修改终端或文本显示的行为.控制字符以 CONTROL + key 组合.控制字符在脚本中不能正常使用.Ctl-B 光标后退,这应该依赖于 bash 输入的风格,默认是 emacs 风格的.Ctl-C Break,终止前台工作.Ctl-D 从当前 shell 登出(和 exit 很像) "EOF"(文件结束符).这也能从 stdin 中终止输入.在 console 或者在 xterm window 中输入的时候,Ctl-D 将删除光标下字符.当没有字符时,Ctrl-D 将退出当前会话.在 xterm window 也有关闭窗口 的效果.Ctl-G beep.在一些老的终端,将响铃.Ctl-H backspace,删除光标前边的字符.如:

1 #!/bin/bash

2 # 在一个变量中插入 Ctl-H

4 a="^H^H"

# 两个 Ctl-H (backspaces).

5 echo "abcdef"

# abcdef

6 echo -n "abcdef$a " # abcdf

7 # 注意结尾的空格

^

^ 两个 twice.

8 echo -n "abcdef$a"

# abcdef

没有 backspace 的效果了(why?).10

# 结果并不像期望的那样 11 echo; echo

Ctl-I 就是 tab 键.Ctl-J 新行.Ctl-K 垂直 tab.(垂直 tab?新颖,没听过) 作用就是删除光标到行尾的字符.Ctl-L clear,清屏.回车 Ctl-M

####Start Script

####

9 #

结尾没有空格

1 #!/bin/bash

2 # Thank you, Lee Maschmeyer, for this example. 3

4 read -n 1 -s -p $'Control-M leaves cursor at beginning of this line.Pre Enter.\x0d'

#当然,'0d'就是二进制的回车. 6 echo >&2

# '-s'参数使得任何输入都不将回显出来

#+ 所以,明确的重起一行是必要的. 8

9 read -n 1 -s -p $'Control-J leaves cursor on next line.\x0a' 10 echo >&2

# Control-J 11

12 ### 13

是换行.14 read -n 1 -s -p $'And Control-K\x0bgoes straight down.' 15 echo >&2

# Control-K 是垂直制表符.16

17 # 关于垂直制表符效果的一个更好的例子见下边: 18

19 var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a' 20 echo "$var" 21 #

这句与上边的例子使用的是同样的办法,然而: 22 echo "$var" | col 23 #

这将造成垂直制表符右边的部分在左边部分的上边.24 #

这也解释了为什么我们要在行首和行尾加上一个换行符-- 25 #+ 来避免一个混乱的屏幕输出.26

27 # Lee Maschmeyer 的解释: 28 # --------------------- 29 #

In the [first vertical tab example] ...the vertical tab 29 #

在这里[第一个垂直制表符的例子中] ... 这个垂直制表符

30 #+ makes the printing go straight down without a carriage return.31 #

This is true only on devices, such as the Linux console, 32 #+ that can't go "backward." 33 #

The real purpose of VT is to go straight UP, not down. 34 #

It can be used to print superscripts on a printer.

34 #

它可以用来在一个打印机上打印上标.35 #

col 的作用,可以用来模仿 VT 的合适的行为.36

37 exit 0

####End Script

###### Ctl-Q Ctl-S Ctl-U Ctl-V

Ctl-W

Ctl-Z

继续(等价于 XON 字符),这个继续的标准输入在一个终端里

挂起(等价于 XOFF 字符),这个被挂起的 stdin 在一个终端里,用 Ctl-Q 恢复 删除光标到行首的所有字符,在某些设置下,删除全行.当输入字符时,Ctl-V 允许插入控制字符.比如,下边 2 个例子是等价的 echo -e '\x0a'

echo Ctl-V 在文本编辑器中十分有用,在 vim 中一样.删除当前光标到前边的最近一个空格之间的字符.在某些设置下,删除到第一个非字母或数字的字符.终止前台工作.空白部分

分割命令或者是变量.包括空格,tab,空行,或任何它们的组合.在一些特殊情况下,空白是不允许的,如变量赋值时,会引起语法错误.空白行在脚本中没有效果."$IFS",对于某些命令输入的特殊变量分割域,默认使用的是空白.如果想保留空白,使用引用.

注意事项: [1]

[2] shell 做大括号的命令扩展.但是命令本身需要对扩展的结果作处理.例外:在 pipe 中的一个大括号中的代码段可能运行在一个子 shell 中.1 ls | { read firstline; read secondline; } 2 #

错误,在打括号中的代码段,将运行到子 shell 中.3 #+ 所以 ls 的输出将不能传递到代码块中.4 echo "First line is $firstline; second line is $secondline"

#

不能工作

[3]

5

6 # Thanks, S.C.

换行符也被认为是空白.这也解释了为什么一个空行也会被认为是空白.第 4 章 变量和参数的介绍

======================

4.1 变量替换 ------------ 变量替换操作符 $ 只有在变量被声明,赋值,unset 或 exported 或者是在变量代表一个 signal 的时候, 变量才会是以本来的面目出现在脚本里.变量在被赋值的时候,可能需要使用"=", read 状态或者是在循环的头部.在""中还是会发生变量替换,这被叫做部分引用,或叫弱引用.而在''中就不会发生变 量替换,这叫做全引用,也叫强引用.具体见第 5 章的讨论.

注意:$var 与${var}的区别,不加{},在某些上下文将引起错误,为了安全,使用 2.具体见 9.3 节 参数替换.

Example 4-1.变量赋值和替换

####Start Script

#### 1 #!/bin/bash 2

3 # 变量赋值和替换

4

5 a=375 6 hello=$a

7

8 #-----------

9 # 强烈注意,在赋值的前后一定不要有空格.10 # 如果有空格会发生什么? 11

12 #

如果"VARIABLE =value",

13 #

^

14 #+ 脚本将尝试运行一个"VARIABLE"的命令,带着一个"=value"参数.15

16 #

如果"VARIABLE= value", 17 #

^

18 #+ script tries to run "value" command with

18 #+ 脚本将尝试运行一个"value"的命令,带着

19 #+ the environmental variable "VARIABLE" set to "".19 #+ 一个被赋成""值的环境变量"VARIABLE".20 #-----------

21

22

23 echo hello

# 没有变量引用,不过是个 hello 字符串 24

25 echo $hello 26 echo ${hello} # 同上

27

28 echo "$hello" 29 echo "${hello}" 30

31 echo 32

33 hello="A B C

D" 34 echo $hello

# A B C D 35 echo "$hello" # A B

C

D 36 # 就象你看到的 echo $hello

echo "$hello"

将给出不同的结果.37 #

^

38 # Quoting a variable preserves whitespace.38 # 引用一个变量将保留其中的空白,当然,如果是变量替换就不会保留了.39

40 echo 41

42 echo '$hello'

# $hello 43 #

^

^ 44 # 全引用的作用

45 #+ 将导致"$"变成一个单独的字符.46

47 # 注意两种引用不同的效果 48

49

50 hello=

# 设置为空值

51 echo "\$hello (null value) = $hello" 52 #

注意设置一个变量为空,与 unset 它,不是一回事,虽然看起来一样 53 # 54

55 # 56

57 #

可以在同一行上设置多个变量.58 #+ 要以空白分隔

59 #

小心,这会降低可读性,和可移植性.60

61 var1=21 var2=22 var3=$V3 62 echo 63 echo "var1=$var1

var2=$var2

var3=$var3" 64

65 # 在老版本的"sh"上,可能会有问题.66

67 # 68

69 echo; echo

^

70

71 numbers="one two three" 72 #

^

^

73 other_numbers="1 2 3" 74 #

^ ^

75 #

如果变量中有空白,那么引用就必要了.76 # 77 echo "numbers = $numbers" 78 echo "other_numbers = $other_numbers"

# other_numbers = 1 2 3 79 echo 80

81 echo "uninitialized_variable = $uninitialized_variable" 82 # Uninitialized 变量为空值(根本就没赋值).

83 uninitialized_variable=

# 声明,但是没被初始化

84

#+ 其实和前边设置为空值得作用是一样的.85 echo "uninitialized_variable = $uninitialized_variable" 86

# 还是一个空值

87

88 uninitialized_variable=23

# 赋值 89 unset uninitialized_variable

# Unset it.90 echo "uninitialized_variable = $uninitialized_variable" 91

# 还是空值

92 echo 93

94 exit 0

####End Script

###### 注意: 一个空值变量,或者是根本就没声明的变量,在赋值之前使用它可能会引起问题.但是还是可以用来做算术运算

####Start Script

#### 1 echo "$uninitialized"

# (blank line) 2 let "uninitialized += 5"

# Add 5 to it. 3 echo "$uninitialized"

# 5

4

5 #

结论: 6 #

对于一个空值变量在做算术操作的时候,就好像它的值为 0 一样.8 #

This is undocumented (and probably non-portable) behavior.7 #

这并没被文档化(可能是不可移植)的行为.

####End Script

###### 具体参考 Example 11-21

4.2 变量赋值

------------ = 赋值操作符(前后都不能有空白) 不要与-eq 混淆,那个是 test,并不是赋值.注意,=也可被用来做 test 操作,这依赖于上下文.

Example 4-2.一般的变量赋值

####Start Script

#### 1 #!/bin/bash

2 # "裸体"变量

4 echo 5

6 # 变量什么时候是"裸体"的,比如前边少了$的时候. 7 # 当它被赋值的时候,而不是被引用的时候. 8

9 # 赋值 10 a=879 11 echo "The value of "a" is $a." 12

13 # 使用 let 赋值 14 let a=16+5 15 echo "The value of "a" is now $a." 16

17 echo 18

19 # 在 for 循环中

20 echo -n "Values of "a" in the loop are: " 21 for a in 7 8 9 11

22 do 23

echo -n "$a " 24 done 25

26 echo 27 echo 28

29 # 在 read 命令状态中 30 echo -n "Enter "a" " 31 read a 32 echo "The value of "a" is now $a." 33

34 echo 35

36 exit 0

####End Script

######

Example 4-3.变量赋值,一般的和比较特殊的

####Start Script

#### 1 #!/bin/bash 2

3 a=23

# Simple case

4 echo $a 5 b=$a 6 echo $b 7

8 # 现在让我们来点小变化

9

10 a=`echo Hello!`

# 把 echo 命令的结果传给变量 a 11 echo $a 12 #

注意,如果在命令扩展结构中使用一个(!)的话,在命令行中将不能工作 13 #+ 因为这触发了 Bash 的"历史机制".14 #

但是,在校本里边使用的话,历史功能是被关闭的,所以就能够正常运行.15 16

17 a=`ls -l`

# 把 ls -l 的结果给 a 18 echo $a

# 别忘了,这么引用的话,ls 的结果中的所有空白部分都没了(包括换行) 19 echo 20 echo "$a"

# 这么引用就正常了,保留了空白 21

# (具体参阅章节"引用") 22

23 exit 0

####End Script

###### 使用$(...)机制进行的变量赋值(除去使用``来赋值的另外一种新方法).事实上这两种方法都是 命令替换的一种形式.# 来自于/ect/rc.d/rc.local R=$(cat /ect/redhat-release) arch=$(uname -m)

4.3 Bash 变量是不分类型的

------------------------ 不像其他程序语言一样,Bash 并不对变量区分"类型".本质上,Bash 变量都是字符串.但是依赖于上下文,Bash 也允许比较操作和算术操作.决定这些的关键因素就是,变量中的值 是否只有数字.

Example 4-4 整型还是 string?

####Start Script

#### 1 #!/bin/bash

2 # int-or-string.sh: 整形还是 string? 3

4 a=2334

# 整型

5 let "a += 1" 6 echo "a = $a "

# a = 2335

7 echo

# 还是整型

9

10 b=${a/23/BB}

# 将

23 替换成 BB

# 这将把 b 变量从整型变为 string 12 echo "b = $b"

# b = BB35 13 declare -i b

# 即使使用 declare 命令也不会对此有任何帮助,9.4 节有解释 14 echo "b = $b"

# b = BB35 15

16 let "b += 1"

# BB35 + 1 = 17 echo "b = $b"

# b = 1

18 echo 19

20 c=BB34 21 echo "c = $c"

# c = BB34 22 d=${c/BB/23}

# S 将 BB 替换成 23 23

# 这使得$d 变为一个整形

24 echo "d = $d"

# d = 2334 25 let "d += 1"

# 2334 + 1 = 26 echo "d = $d"

# d = 2335 27 echo 28

29 # 关于空变量怎么样? 30 e="" 31 echo "e = $e"

# e =

32 let "e += 1"

# 算术操作允许一个空变量? 33 echo "e = $e"

# e = 1

34 echo

# 空变量将转换成一个整型变量

35

36 # 关于未声明的变量怎么样? 37 echo "f = $f"

# f =

38 let "f += 1"

# 算术操作允许么? 39 echo "f = $f"

# f = 1

40 echo

# 未声明的变量将转换成一个整型变量

41

42

43

44 # 所以说 Bash 中的变量都是无类型的.45

46 exit 0

####End Script

######

4.4 特殊的变量类型

------------------ local variables 这种变量只有在代码块或者是函数中才可见(具体见 23.2 和 23 章) environmental variables 这种变量将改变用户接口和 shell 的行为.

在一般的上下文中,每个进程都有自己的环境,就是一组保持进程可能引用的信息的 变量.这种情况下,shell 于一个一般进程是相同的.

每次当 shell 启动时,它都将创建自己的环境变量.更新或者添加新的环境变量,将导 致 shell 更新它的环境,同时也会影响所有继承自这个环境的所有子进程(由这个命令 导致的).

注意:分配给环境变量的空间是受限的.创建太多的环境变量将引起空间溢出,这会引 起问题.关于 eval 命令,具体见第 11 章

bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`" bash$ du bash: /usr/bin/du: Argument list too long

如果一个脚本设置了环境变量,需要 export 它,来通知本脚本的环境,这是 export 命令的功能,关于 export 命令,具体见 11 章.

脚本只能对它产生的子进程 export 变量.一个从命令行被调用的脚本 export 的变量,将 不能影响调用这个脚本的那个命令行 shell 的环境.

positional parameters 就是从命令行中传进来的参数,$0, $1, $2, $3...

$0 就是脚本文件的名字,$1 是第一个参数,$2 为第 2 个...,参见[1](有$0 的说明),$9 以后就需要打括号了,如${10},${11},${12}...两个值得注意的变量$*和$@(第 9 章有具体的描述),表示所有的位置参数.

Example 4-5 位置参数

####Start Script

#### 1 #!/bin/bash 2

3 # 作为用例,调用这个脚本至少需要 10 个参数,如

4 # ./scriptname 1 2 3 4 5 6 7 8 9 10

5 MINPARAMS=10 6

7 echo 8

9 echo "The name of this script is "$0"." 10 # 添加./是为了当前目录

11 echo "The name of this script is "`basename $0`"." 12 # 去掉目录信息,具体见'basename'命令 13

14 echo 15

16 if [ -n "$1" ]

# 测试变量被被引用 17 then 18

echo "Parameter #1 is $1"

# "#"没被转义 19 fi

20

21 if [ -n "$2" ] 22 then 23

echo "Parameter #2 is $2" 24 fi

25

26 if [ -n "$3" ] 27 then 28

echo "Parameter #3 is $3" 29 fi

30

31 # ...32

33

34 if [ -n "${10}" ]

# 大于 9 的参数必须出现在{}中.35 then 36

echo "Parameter #10 is ${10}" 37 fi

38

39 echo "----" 40 echo "All the command-line parameters are: "$*"" 41

42 if [ $# -lt "$MINPARAMS" ]

#$#是传到脚本里的位置参数的个数 43 then 44

echo 45

echo "This script needs at least $MINPARAMS command-line arguments!" 46 fi

47

48 echo 49

50 exit 0

####End Script

###### {}标记法是一种很好的使用位置参数的方法.这也需要间接引用(见

Example 34-2) 1 args=$#

# 位置参数的个数

2 lastarg=${!args} 3 # 或:

lastarg=${!#} 4 # 注意 lastarg=${!$#} 将报错

一些脚本可能会依赖于使用不同的调用名字,而表现出不同的行为,这样一般都需要 判断$0,而其他的名字都是通过 ln 命令产生的链接.(具体参见 Example 12-2)

如果脚本需要一个命令行参数,而调用的时候,没用这个参数,这就有可能造成分配一个 空变量,这样估计就会引起问题.一种解决办法就是在这个位置参数,和相关的变量后 边,都添加一个额外的字符.具体见下边的例子.

####Start Script

#### 1 variable1_=$1_ #而不是 variable1=$1 2 # 这将阻止一个错误,即使在调用时没使用这个位置参数. 3

4 critical_argument01=$variable1_ 5

6 # 这个扩展的字符是可以被消除掉的,就像这样. 7 variable1=${variable1_/_/} 8 # 副作用就是$variable1_多了一个下划线

9 # 这里使用了一个参数替换模版(后边会有具体的讨论) 10 # (Leaving out the replacement pattern results in a deletion.)

10 # (在一个删除动作中,节省了一个替换模式) 11

12

13 # 一个解决这种问题的更简单的做法就是,判断一下这个位置参数是否传递下来了 14 if [ -z $1 ] 15 then 16

exit $E_MISSING_POS_PARAM 17 fi 18

19

20 #

但是上边的方法将可能产生一个意外的副作用 21 #

参数替换的更好的办法应该是: 22 #

${1:-$DefaultVal} 23 #

具体察看"Parameter Substition"节 24 #+ 在第 9 章

####End Script

######

Example 4-6 wh,whois 节点名字查询

####Start Script

#### 1 #!/bin/bash

2 # ex18.sh

3

4 # Does a 'whois domain-name' lookup on any of 3 alternate servers:

5 #

ripe.net, cw.net, radb.net

6

7 # 把这个脚本重命名为'wh',然后放到/usr/local/bin 下

8

9 # 需要 3 个符号链接

10 # ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe 11 # ln -s /usr/local/bin/wh /usr/local/bin/wh-cw 12 # ln -s /usr/local/bin/wh /usr/local/bin/wh-radb 13

14 E_NOARGS=65 15

16

17 if [ -z "$1" ] 18 then 19

echo "Usage: `basename $0` [domain-name]" 20

exit $E_NOARGS 21 fi 22

23 # Check script name and call proper server.23 # 检查脚本名字,然后调用合适的服务器

24 case `basename $0` in

# Or:

case ${0##*/} in 25

"wh"

) whois $1@whois.ripe.net;;

26

"wh-ripe") whois $1@whois.ripe.net;; 27

"wh-radb") whois $1@whois.radb.net;; 28

"wh-cw" ) whois $1@whois.cw.net;; 29

*

) echo "Usage: `basename $0` [domain-name]";; 30 esac

注册表脚本编程入门

LinuxShell脚本编程实例

Android 4高级编程

C语言高级编程心得体会

《网络高级编程》实验报告要求

Linux高级编程实训项目

高级财务人员能力指南

高级财务人员能力指南

类Pascal脚本编写指南[版]

C语言高级编程及实例剖析

高级Bash脚本编程指南
《高级Bash脚本编程指南.doc》
将本文的Word文档下载到电脑,方便编辑。
推荐度:
点击下载文档
点击下载本文文档