高级Bash脚本编程指南

一个对脚本编程技术的深入探讨

版本 3.7.3

2005年11月28日

Mendel Cooper


thegrendel@theriver.com

本指南不期望读者有任何的脚本和编程的背景知识,但能引导你迅速提高到中高级水平的教程 . . . 所有这些只是 UNIX®浩瀚知识的一小部分. 本指南可做为shell脚本技术的教科书,自学指导书或是技术参考书。教程中的练习和很好的注释能激起读者积极参与,但前提是要认识到:只有真正动手去写脚本才是唯一真正学会脚本编程技术的方法.

本书非常适用于教学编程概念的常规指导。

本书的最新版本,以归档格式提供, bzip2-ed 格式包提供了SGML源码和额外的HTML格式,可以从作者的主页上取得。也提供了 PDF版本 ,可查看更新日志 来了解版本历史。


译者序

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

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

译者 杨春敏 黄毅
2006.5.28


贡献

献给Anita,我所有动力的源泉。

目录
第一部分: 绪论
1. 为什么要有Shell编程?
2. 2.从一个Sha-Bang开始
2.1. 运行脚本
2.2. 预备练习
第二部分: 基础
3. 特殊字符
4. 变量和参数介绍
4.1. 变量替换
4.2. 变量赋值
4.3. Bash变量是无类型的
4.4. 特殊变量类型
5. 引用
5.1. 引用变量
5.2. 转义
6. 退出和退出状态
7. 测试
7.1. 测试结构
7.2. 文件测试操作符
7.3. 其他比较操作符
7.4. 嵌套的if/then条件测试
7.5. 检验你对测试(test)的认识
8. 操作符及相关主题
8.1. 操作符
8.2. 数值常量
第三部分: 进阶
9. 访问变量
9.1. 内部变量
9.2. 字符串处理
9.3. 参数替换
9.4. 指定变量的类型: 用 declaretypeset
9.5. 间接变量引用
9.6. $RANDOM: 产生随机整数
9.7. 双圆括号结构
10. 循环和分支
10.1. 循环
10.2. 嵌套循环
10.3. 循环控制
10.4. 判断及分支
11. 内部命令与内建命令
11.1. 作业控制命令
12. 外部过滤器,程序与命令
12.1. 基本命令
12.2. 复杂命令
12.3. 时间/日期命令
12.4. 文本处理命令
12.5. 文件与归档命令
12.6. 通信命令
12.7. 终端控制命令
12.8. 数学命令
12.9. 其他的杂项命令
13. 系统和管理员命令
13.1. 分析一个系统脚本
14. 命令替换
15. 算术扩展
16. I/O重定向
16.1. 使用exec
16.2. 重定向代码阻塞
16.3. 应用
17. Here Documents
17.1. Here Strings
18. 中场休息
第四部分: 高级主题
19. 正则表达式
19.1. 正则表达式简介
19.2. 通配
20. 子shell
21. 受限shell
22. 进程替换
23. 函数
23.1. 复杂函数与函数复杂性
23.2. 局部变量
23.3. 没有局部变量的递归
24. 别名
25. 列表结构
26. 数组
27. /dev和/proc
27.1. /dev
27.2. /proc
28. 关于Zeros和Nulls
29. 调试
30. 选项
31. Gotchas
32. 脚本风格
32.1. 非官方的脚本风格
33. 杂项
33.1. 交互与非交互的shell和脚本
33.2. Shell包装
33.3. 测试和比较: 另一种方法
33.4. 递归
33.5. "彩色" 脚本
33.6. 优化
33.7. 多种小技术
33.8. 安全主题
33.9. 可移植性话题
33.10. Windows下的脚本编程
34. Bash,版本2和3
34.1. Bash, 版本2
34.2. Bash, 版本3
35. 后记
35.1. 作者后记
35.2. 关于作者
35.3. 哪里可以取得帮助?
35.4. 制作这本书的工具
35.4.1. 硬件
35.4.2. 软件和排版软件
35.5. 致谢
参考书
A. 贡献的脚本
B. 参考卡片
C. 一个Sed和Awk的初级精短读本
C.1. Sed
C.2. Awk
D. Exit Codes With Special Meanings
E. 一个关于I/O和I/O重定向的简介
F. 标准命令行选项
G. 重要文件
H. 重要系统目录
I. 本地化
J. 历史命令
K. 一个.bashrc例子文件
L. 把DOS批处理文件转换成Shell脚本
M. 练习
M.1. 脚本分析
M.2. 写脚本
N. 修改历史命令
O. 镜像站点
P. 将完成的内容列表
Q. 版权
书中的表格清单
11-1. 作业控制标识
30-1. Bash选项
33-1. 转义序列中数值和彩色的对应表
B-1. 特殊Shell变量表
B-2. 测试操作符: 二元比较操作
B-3. 测试操作符: 文件操作
B-4. 参数替换和扩展
B-5. 字符串操作
B-6. 杂项结构
C-1. 基本的sed操作符
C-2. sed操作符的例子
D-1. "保留的通用" 退出代码值
L-1. DOS的批处理文件关键字 / 变量名 / 操作符, 及shell等价对应表
L-2. DOS 命令和 UNIX 等价命令对应表
N-1. 修订历史
例子脚本列表
2-1. cleanup: A script to clean up the log files in /var/log
2-2. cleanup: An improved clean-up script
2-3. cleanup: An enhanced and generalized version of above scripts.
3-1. Code blocks and I/O redirection
3-2. Saving the results of a code block to a file
3-3. Running a loop in the background
3-4. Backup of all files changed in last day
4-1. Variable assignment and substitution
4-2. Plain Variable Assignment
4-3. Variable Assignment, plain and fancy
4-4. Integer or string?
4-5. Positional Parameters
4-6. wh, whois domain name lookup
4-7. Using shift
5-1. Echoing Weird Variables
5-2. Escaped Characters
6-1. exit / exit status
6-2. Negating a condition using !
7-1. What is truth?
7-2. Equivalence of test, /usr/bin/test, [ ], and /usr/bin/[
7-3. Arithmetic Tests using (( ))
7-4. Testing for broken links
7-5. Arithmetic and string comparisons
7-6. Testing whether a string is null
7-7. zmore
8-1. Greatest common divisor
8-2. Using Arithmetic Operations
8-3. Compound Condition Tests Using && and ||
8-4. Representation of numerical constants
9-1. $IFS and whitespace
9-2. Timed Input
9-3. Once more, timed input
9-4. Timed read
9-5. Am I root?
9-6. arglist: Listing arguments with $* and $@
9-7. Inconsistent $* and $@ behavior
9-8. $* and $@ when $IFS is empty
9-9. Underscore variable
9-10. Inserting a blank line between paragraphs in a text file
9-11. Converting graphic file formats, with filename change
9-12. Emulating getopt
9-13. Alternate ways of extracting substrings
9-14. Using parameter substitution and error messages
9-15. Parameter substitution and "usage" messages
9-16. Length of a variable
9-17. Pattern matching in parameter substitution
9-18. Renaming file extensions:
9-19. Using pattern matching to parse arbitrary strings
9-20. Matching patterns at prefix or suffix of string
9-21. Using declare to type variables
9-22. Indirect References
9-23. Passing an indirect reference to awk
9-24. Generating random numbers
9-25. Picking a random card from a deck
9-26. Random between values
9-27. Rolling a single die with RANDOM
9-28. Reseeding RANDOM
9-29. Pseudorandom numbers, using awk
9-30. C-type manipulation of variables
10-1. Simple for loops
10-2. for loop with two parameters in each [list] element
10-3. Fileinfo: operating on a file list contained in a variable
10-4. Operating on files with a for loop
10-5. Missing in [list] in a for loop
10-6. Generating the [list] in a for loop with command substitution
10-7. A grep replacement for binary files
10-8. Listing all users on the system
10-9. Checking all the binaries in a directory for authorship
10-10. Listing the symbolic links in a directory
10-11. Symbolic links in a directory, saved to a file
10-12. A C-like for loop
10-13. Using efax in batch mode
10-14. Simple while loop
10-15. Another while loop
10-16. while loop with multiple conditions
10-17. C-like syntax in a while loop
10-18. until loop
10-19. Nested Loop
10-20. Effects of break and continue in a loop
10-21. Breaking out of multiple loop levels
10-22. Continuing at a higher loop level
10-23. Using "continue N" in an actual task
10-24. Using case
10-25. Creating menus using case
10-26. Using command substitution to generate the case variable
10-27. Simple string matching
10-28. Checking for alphabetic input
10-29. Creating menus using select
10-30. Creating menus using select in a function
11-1. A script that forks off multiple instances of itself
11-2. printf in action
11-3. Variable assignment, using read
11-4. What happens when read has no variable
11-5. Multi-line input to read
11-6. Detecting the arrow keys
11-7. Using read with file redirection
11-8. Problems reading from a pipe
11-9. Changing the current working directory
11-10. Letting "let" do arithmetic.
11-11. Showing the effect of eval
11-12. Forcing a log-off
11-13. A version of "rot13"
11-14. Using eval to force variable substitution in a Perl script
11-15. Using set with positional parameters
11-16. Reassigning the positional parameters
11-17. "Unsetting" a variable
11-18. Using export to pass a variable to an embedded awk script
11-19. Using getopts to read the options/arguments passed to a script
11-20. "Including" a data file
11-21. A (useless) script that sources itself
11-22. Effects of exec
11-23. A script that exec's itself
11-24. Waiting for a process to finish before proceeding
11-25. A script that kills itself
12-1. Using ls to create a table of contents for burning a CDR disk
12-2. Hello or Good-bye
12-3. Badname, eliminate file names in current directory containing bad characters and whitespace.
12-4. Deleting a file by its inode number
12-5. Logfile: Using xargs to monitor system log
12-6. Copying files in current directory to another
12-7. Killing processes by name
12-8. Word frequency analysis using xargs
12-9. Using expr
12-10. Using date
12-11. Word Frequency Analysis
12-12. Which files are scripts?
12-13. Generating 10-digit random numbers
12-14. Using tail to monitor the system log
12-15. Emulating "grep" in a script
12-16. Looking up definitions in Webster's 1913 Dictionary
12-17. Checking words in a list for validity
12-18. toupper: Transforms a file to all uppercase.
12-19. lowercase: Changes all filenames in working directory to lowercase.
12-20. Du: DOS to UNIX text file conversion.
12-21. rot13: rot13, ultra-weak encryption.
12-22. Generating "Crypto-Quote" Puzzles
12-23. Formatted file listing.
12-24. Using column to format a directory listing
12-25. nl: A self-numbering script.
12-26. manview: Viewing formatted manpages
12-27. Using cpio to move a directory tree
12-28. Unpacking an rpm archive
12-29. Stripping comments from C program files
12-30. Exploring /usr/X11R6/bin
12-31. An "improved" strings command
12-32. Using cmp to compare two files within a script.
12-33. basename and dirname
12-34. Checking file integrity
12-35. Uudecoding encoded files
12-36. Finding out where to report a spammer
12-37. Analyzing a spam domain
12-38. Getting a stock quote
12-39. Updating FC4
12-40. Using ssh
12-41. A script that mails itself
12-42. Monthly Payment on a Mortgage
12-43. Base Conversion
12-44. Invoking bc using a "here document"
12-45. Calculating PI
12-46. Converting a decimal number to hexadecimal
12-47. Factoring
12-48. Calculating the hypotenuse of a triangle
12-49. Using seq to generate loop arguments
12-50. Letter Count"
12-51. Using getopt to parse command-line options
12-52. A script that copies itself
12-53. Exercising dd
12-54. Capturing Keystrokes
12-55. Securely deleting a file
12-56. Filename generator
12-57. Converting meters to miles
12-58. Using m4
13-1. Setting a new password
13-2. Setting an erase character
13-3. secret password: Turning off terminal echoing
13-4. Keypress detection
13-5. Checking a remote server for identd
13-6. pidof helps kill a process
13-7. Checking a CD image
13-8. Creating a filesystem in a file
13-9. Adding a new hard drive
13-10. Using umask to hide an output file from prying eyes
13-11. killall, from /etc/rc.d/init.d
14-1. Stupid script tricks
14-2. Generating a variable from a loop
14-3. Finding anagrams
16-1. Redirecting stdin using exec
16-2. Redirecting stdout using exec
16-3. Redirecting both stdin and stdout in the same script with exec
16-4. Avoiding a subshell
16-5. Redirected while loop
16-6. Alternate form of redirected while loop
16-7. Redirected until loop
16-8. Redirected for loop
16-9. Redirected for loop (both stdin and stdout redirected)
16-10. Redirected if/then test
16-11. Data file "names.data" for above examples
16-12. Logging events
17-1. broadcast: Sends message to everyone logged in
17-2. dummyfile: Creates a 2-line dummy file
17-3. Multi-line message using cat
17-4. Multi-line message, with tabs suppressed
17-5. Here document with parameter substitution
17-6. Upload a file pair to "Sunsite" incoming directory
17-7. Parameter substitution turned off
17-8. A script that generates another script
17-9. Here documents and functions
17-10. "Anonymous" Here Document
17-11. Commenting out a block of code
17-12. A self-documenting script
17-13. Prepending a line to a file
20-1. Variable scope in a subshell
20-2. List User Profiles
20-3. Running parallel processes in subshells
21-1. Running a script in restricted mode
23-1. Simple functions
23-2. Function Taking Parameters
23-3. Functions and command-line args passed to the script
23-4. Passing an indirect reference to a function
23-5. Dereferencing a parameter passed to a function
23-6. Again, dereferencing a parameter passed to a function
23-7. Maximum of two numbers
23-8. Converting numbers to Roman numerals
23-9. Testing large return values in a function
23-10. Comparing two large integers
23-11. Real name from username
23-12. Local variable visibility
23-13. Recursion, using a local variable
23-14. The Towers of Hanoi
24-1. Aliases within a script
24-2. unalias: Setting and unsetting an alias
25-1. Using an "and list" to test for command-line arguments
25-2. Another command-line arg test using an "and list"
25-3. Using "or lists" in combination with an "and list"
26-1. Simple array usage
26-2. Formatting a poem
26-3. Various array operations
26-4. String operations on arrays
26-5. Loading the contents of a script into an array
26-6. Some special properties of arrays
26-7. Of empty arrays and empty elements
26-8. Initializing arrays
26-9. Copying and concatenating arrays
26-10. More on concatenating arrays
26-11. An old friend: The Bubble Sort
26-12. Embedded arrays and indirect references
26-13. Complex array application: Sieve of Eratosthenes
26-14. Emulating a push-down stack
26-15. Complex array application: Exploring a weird mathematical series
26-16. Simulating a two-dimensional array, then tilting it
27-1. Using /dev/tcp for troubleshooting
27-2. Finding the process associated with a PID
27-3. On-line connect status
28-1. Hiding the cookie jar
28-2. Setting up a swapfile using /dev/zero
28-3. Creating a ramdisk
29-1. A buggy script
29-2. Missing keyword
29-3. test24, another buggy script
29-4. Testing a condition with an "assert"
29-5. Trapping at exit
29-6. Cleaning up after Control-C
29-7. Tracing a variable
29-8. Running multiple processes (on an SMP box)
31-1. Numerical and string comparison are not equivalent
31-2. Subshell Pitfalls
31-3. Piping the output of echo to a read
33-1. shell wrapper
33-2. A slightly more complex shell wrapper
33-3. A generic shell wrapper that writes to a logfile
33-4. A shell wrapper around an awk script
33-5. A shell wrapper around another awk script
33-6. Perl embedded in a Bash script
33-7. Bash and Perl scripts combined
33-8. A (useless) script that recursively calls itself
33-9. A (useful) script that recursively calls itself
33-10. Another (useful) script that recursively calls itself
33-11. A "colorized" address database
33-12. Drawing a box
33-13. Echoing colored text
33-14. A "horserace" game
33-15. Return value trickery
33-16. Even more return value trickery
33-17. Passing and returning arrays
33-18. Fun with anagrams
33-19. Widgets invoked from a shell script
34-1. String expansion
34-2. Indirect variable references - the new way
34-3. Simple database application, using indirect variable referencing
34-4. Using arrays and other miscellaneous trickery to deal four random hands from a deck of cards
A-1. mailformat: Formatting an e-mail message
A-2. rn: A simple-minded file rename utility
A-3. blank-rename: renames filenames containing blanks
A-4. encryptedpw: Uploading to an ftp site, using a locally encrypted password
A-5. copy-cd: Copying a data CD
A-6. Collatz series
A-7. days-between: Calculate number of days between two dates
A-8. Make a "dictionary"
A-9. Soundex conversion
A-10. "Game of Life"
A-11. Data file for "Game of Life"
A-12. behead: Removing mail and news message headers
A-13. ftpget: Downloading files via ftp
A-14. password: Generating random 8-character passwords
A-15. fifo: Making daily backups, using named pipes
A-16. Generating prime numbers using the modulo operator
A-17. tree: Displaying a directory tree
A-18. string functions: C-like string functions
A-19. Directory information
A-20. Object-oriented database
A-21. Library of hash functions
A-22. Colorizing text using hash functions
A-23. Mounting USB keychain storage devices
A-24. Preserving weblogs
A-25. Protecting literal strings
A-26. Unprotecting literal strings
A-27. Spammer Identification
A-28. Spammer Hunt
A-29. Making wget easier to use
A-30. A "podcasting" script
A-31. Basics Reviewed
A-32. An expanded cd command
C-1. Counting Letter Occurrences
K-1. Sample .bashrc file
L-1. VIEWDATA.BAT: DOS Batch File
L-2. viewdata.sh: Shell Script Conversion of VIEWDATA.BAT
P-1. Print the server environment