高级Bash脚本编程指南
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                高级Bash脚本编程指南
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.                        
                                http://www.linuxdiyf.com/viewarticle.php?id=18812
毫無疑問,UNIX/Linux最重要的軟件之一就是shell,目前最流行的shell被稱為Bash(Bourne Again Shell),幾乎所有的Linux和絕大部分的UNIX都可以使用Bash。作為系統(tǒng)與用戶之間的交互接口,shell幾乎是你在UNIX工作平臺(tái)上最親密的朋友,因此,學(xué)好shell,是學(xué)習(xí)Linux/UNIX的的開始,并且它會(huì)始終伴隨你的工作學(xué)習(xí)。shell是如此地重要,但令人驚奇的是,介紹shell的書沒有真正令人滿意的。所幸的是,我看到了這本被人稱為abs的書,這本書介紹了bash大量的細(xì)節(jié)和廣闊的范圍,我遇到的絕大部分的技術(shù)問題--無論是我忘記的或是以前沒有發(fā)現(xiàn)的--都可以在這本書里找到答案。這本使用大量的例子詳細(xì)地介紹了Bash的語法,各種技巧,調(diào)試等等的技術(shù),以循序漸進(jìn)的學(xué)習(xí)方式,讓你了解Bash的所有特性,在書中還有許多練習(xí)可以引導(dǎo)你思考,以得到更深入的知識(shí)。無論你是新手還是老手,或是使用其他語言的程序員,我能肯定你能在此書用受益。而本書除了介紹BASH的知識(shí)之外,也有許多有用的關(guān)于Linux/UNIX的知識(shí)和其他shell的介紹。
在看到本書的英文版后,我決定把它翻譯出來,在Linuxsir論壇上結(jié)識(shí)了譯者之一楊春敏共同翻譯這本書,600多頁的書是本大部頭的書,我們花了6個(gè)月的業(yè)余時(shí)間才翻譯完了。
關(guān)于版權(quán)的問題,英文版的作者M(jìn)endel Cooper對(duì)英文版的版權(quán)做了詳細(xì)的約定,請(qǐng)參考:Appendix Q. Copyright。中文版版權(quán)由譯者楊春敏和黃毅共同所有,在遵守英文版版權(quán)相應(yīng)條款的條件下,歡迎在保留本書譯者名字和版權(quán)說明以非盈利的方式自由發(fā)布此中文版,以盈利目的的所有行為必須聯(lián)系英文作者和兩位中文譯者以獲得許可。
本書得以成稿,我(黃毅)要多謝我的女朋友,本該給予她的時(shí)間我用來了翻譯,多謝你的理解,你是一個(gè)很棒的女朋友!
譯者 楊春敏 黃毅
2006.5.15
Advanced Bash-Scripting Guide
<<高級(jí)Bash腳本編程指南>>
一本深入學(xué)習(xí)shell腳本藝術(shù)的書籍
Version 3.7.2
2005/11/16
作者:Mendel Cooper
mail:thegrendel@theriver.com
這本書假定你沒有任何腳本或一般程序的編程知識(shí),但是如果你有相關(guān)的知識(shí),那么你將很容易
達(dá)到中高級(jí)的水平...all the while sneaking in little snippets of UNIX? wisdom and
lore(這句不知道怎么譯).你可以把本書作為教材,自學(xué)手冊(cè),或者你獲得shell腳本技術(shù)的文檔.
書中的練習(xí)和例子腳本中的注釋將會(huì)與讀者有更好的互動(dòng),但是最關(guān)鍵的前提是:
想真正學(xué)習(xí)腳本編程的唯一途徑就是編寫腳本.
這本書也可作為教材來講解一般的編程概念.
下載本書最新版本, http://personal.riverusers.com/~thegrendel/abs-guide-3.7.tar.bz2,
這是一個(gè)以tar和bzip2進(jìn)行打包的,并且是以HTML來發(fā)行的.當(dāng)然,你也可以獲得本書的pdf版本
在 http://www.tldp.org/LDP/abs/abs-guide.pdf.可以在
http://personal.riverusers.com/~thegrendel/Change.log中查看修訂歷史.
譯者:楊春敏,黃毅
mail:chunmin.yang@gmail.com
一直想好好學(xué)習(xí)一下bash,可惜網(wǎng)上的資料都雜亂不堪,我還是喜歡通過一本書系統(tǒng)的學(xué)習(xí).這本
書來得正是時(shí)候.本書的作者真是非常的嚴(yán)謹(jǐn),從例子里的改進(jìn)人名單就能看出來.可惜我水平真
的是非常有限,好多地方估計(jì)譯得都有問題.希望閱讀的朋友們多多提些修改建議.我會(huì)盡我的最
大努力去修正它.
目錄
++++
第一部分. 熱身
1. 為什么使用shell編程
2. 帶著一個(gè)Sha-Bang出發(fā)(Sha-Bang指的是#!)
2.1. 調(diào)用一個(gè)腳本
2.2. 初步的練習(xí)
第二部分. 基本
3. 特殊字符
4. 變量和參數(shù)的介紹
4.1. 變量替換
4.2. 變量賦值
4.3. Bash變量是不分類型的
4.4. 特殊的變量類型
5. 引用(翻譯的可能有問題,特指引號(hào))
5.1. 引用變量
5.2. 轉(zhuǎn)義(\)
6. 退出和退出狀態(tài)
7. Tests
7.1. Test結(jié)構(gòu)
7.2. 文件測(cè)試操作
7.3. 其他比較操作
7.4. 嵌套的if/then條件test
7.5. 檢查你的test知識(shí)
8. 操作符和相關(guān)的主題
8.1. 操作符
8.2. 數(shù)字常量
第三部分. 超越基本
9. 變量重游
9.1. 內(nèi)部變量
9.2. 操作字符串
9.3. 參數(shù)替換
9.4. 指定類型的變量:declare或者typeset
9.5. 變量的間接引用
9.6. $RANDOM: 產(chǎn)生隨機(jī)整數(shù)
9.7. 雙圓括號(hào)結(jié)構(gòu)
10. 循環(huán)和分支
10.1. 循環(huán)
10.2. 嵌套循環(huán)
10.3. 循環(huán)控制
10.4. 測(cè)試與分支(case和select結(jié)構(gòu))
11. 內(nèi)部命令與內(nèi)建
11.1. 作業(yè)控制命令
12. 外部過濾器,程序和命令
12.1. 基本命令
12.2. 復(fù)雜命令
12.3. 時(shí)間/日期 命令
12.4. 文本處理命令
12.5. 文件與歸檔命令
12.6. 通訊命令
12.7. 終端控制命令
12.8. 數(shù)學(xué)計(jì)算命令
12.9. 混雜命令
13. 系統(tǒng)與管理命令
13.1. 分析一個(gè)系統(tǒng)腳本
14. 命令替換
15. 算術(shù)擴(kuò)展
16. I/O 重定向
16.1. 使用exec
16.2. 代碼塊的重定向
16.3. 應(yīng)用
17. Here Documents
17.1. Here Strings
18. 休息時(shí)間
Part 4. 高級(jí)
19. 正則表達(dá)式
19.1. 一個(gè)簡要的正則表達(dá)式介紹
19.2. 通配
20. 子shell(Subshells)
21. 受限shell(Restricted Shells)
22. 進(jìn)程替換
23. 函數(shù)
23.1. 復(fù)雜函數(shù)和函數(shù)復(fù)雜性
23.2. 局部變量
23.3. 不使用局部變量的遞歸
24. 別名(Aliases)
25. 列表結(jié)構(gòu)
26. 數(shù)組
27. /dev 和 /proc
27.1. /dev
27.2. /proc
28. 關(guān)于Zeros和Nulls
29. 調(diào)試
30. 選項(xiàng)
31. Gotchas
32. 腳本編程風(fēng)格
32.1. 非官方的Shell腳本風(fēng)格
33. 雜項(xiàng)
33.1. 交互式和非交互式的shells和腳本
33.2. Shell 包裝
33.3. 測(cè)試和比較: 另一種方法
33.4. 遞歸
33.5. 彩色腳本
33.6. 優(yōu)化
33.7. 各種小技巧
33.8. 安全話題
33.8.1. 被感染的腳本
33.8.2. 隱藏Shell腳本源碼
33.9. 移植話題
33.10. 在Windows下進(jìn)行Shell編程
34. Bash, 版本 2 和 3
34.1. Bash, 版本2
34.2. Bash, 版本3
35. 后記
35.1. 作者后記
35.2. 關(guān)于作者
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. 作業(yè)標(biāo)識(shí)符
30-1. Bash 選項(xiàng)
33-1. 轉(zhuǎn)義序列中數(shù)值和彩色的對(duì)應(yīng)
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. 清除:一個(gè)改良的清除腳本
2-3. cleanup:一個(gè)增強(qiáng)的和廣義的刪除logfile的腳本
3-1. 代碼塊和I/O重定向
3-2. 將一個(gè)代碼塊的結(jié)果保存到文件
3-3. 在后臺(tái)運(yùn)行一個(gè)循環(huán)
3-4. 備份最后一天所有修改的文件.
4-1. 變量賦值和替換
4-2. 一般的變量賦值
4-3. 變量賦值,一般的和比較特殊的
4-4. 整型還是string?
4-5. 位置參數(shù)
4-6. wh,whois節(jié)點(diǎn)名字查詢
4-7. 使用shift
5-1. echo一些詭異的變量
5-2. 轉(zhuǎn)義符
6-1. exit/exit狀態(tài)
6-2. 否定一個(gè)條件使用!
7-1. 什么情況下為真?
7-2. 幾個(gè)等效命令test,/usr/bin/test,[],和/usr/bin/[
7-3. 算數(shù)測(cè)試使用(( ))
7-4. test死的鏈接文件
7-5. 數(shù)字和字符串比較
7-6. 測(cè)試字符串是否為null
7-7. zmore
8-1. 最大公約數(shù)
8-2. 使用算術(shù)操作符
8-3. 使用&&和||進(jìn)行混合狀態(tài)的test
8-4. 數(shù)字常量的處理
9-1. $IFS和空白
9-2. 時(shí)間輸入
9-3. 再來一個(gè)時(shí)間輸入
9-4. Timed read
9-5. 我是root?
9-6. arglist:通過$*和$@列出所有的參數(shù)
9-7. 不一致的$*和$@行為
9-8. 當(dāng)$IFS為空時(shí)的$*和$@
9-9. 下劃線變量
9-10. 在一個(gè)文本文件的段間插入空行
9-11. 利用修改文件名,來轉(zhuǎn)換圖片格式
9-12. 模仿getopt命令
9-13. 提取字符串的一種可選的方法
9-14. 使用參數(shù)替換和error messages
9-15. 參數(shù)替換和"usage"messages
9-16. 變量長度
9-17. 參數(shù)替換中的模式匹配
9-18. 重命名文件擴(kuò)展名
9-19. 使用模式匹配來分析比較特殊的字符串
9-20. 對(duì)字符串的前綴或后綴使用匹配模式
9-21. 使用declare來指定變量的類型
9-22. 間接引用
9-23. 傳遞一個(gè)間接引用給awk
9-24. 產(chǎn)生隨機(jī)數(shù)
9-25. 從一副撲克牌中取出一張隨機(jī)的牌
9-26. 兩個(gè)指定值之間的隨機(jī)數(shù)
9-27. 使用隨機(jī)數(shù)來搖一個(gè)骰子
9-28. 重新分配隨機(jī)數(shù)種子
9-29. 使用awk產(chǎn)生偽隨機(jī)數(shù)
9-30. C風(fēng)格的變量處理
10-1. 循環(huán)的一個(gè)簡單例子
10-2. 每個(gè)
- 元素帶兩個(gè)參數(shù)的for循環(huán)
 10-3. 文件信息:對(duì)包含在變量中的文件列表進(jìn)行操作
 10-4. 在for循環(huán)中操作文件
 10-5. 在for循環(huán)中省略
 10-6. 使用命令替換來產(chǎn)生for循環(huán)的
 10-7. 對(duì)于二進(jìn)制文件的一個(gè)grep替換
 10-8. 列出系統(tǒng)上的所有用戶
 10-9. 在目錄的所有文件中查找源字串
 10-10. 列出目錄中所有的符號(hào)連接文件
 10-11. 將目錄中的符號(hào)連接文件名保存到一個(gè)文件中
 10-12. 一個(gè)C風(fēng)格的for循環(huán)
 10-13. 在batch mode中使用efax
 10-14. 簡單的while循環(huán)
 10-15. 另一個(gè)while循環(huán)
 10-16. 多條件的while循環(huán)
 10-17. C風(fēng)格的while循環(huán)
 10-18. until循環(huán)
 10-19. 嵌套循環(huán)
 10-20. break和continue命令在循環(huán)中的效果
 10-21. 多層循環(huán)的退出
 10-22. 多層循環(huán)的continue
 10-23. 在實(shí)際的任務(wù)中使用"continue N"
 10-24. 使用case
 10-25. 使用case來創(chuàng)建菜單
 10-26. 使用命令替換來產(chǎn)生case變量
 10-27. 簡單字符串匹配
 10-28. 檢查是否是字母輸入
 10-29. 用select來創(chuàng)建菜單
 10-30. 用函數(shù)中select結(jié)構(gòu)來創(chuàng)建菜單
 11-1. 一個(gè)fork出多個(gè)自己實(shí)例的腳本
 11-2. printf
 11-3. 使用read,變量分配
 11-4. 當(dāng)使用一個(gè)不帶變量參數(shù)的read命令時(shí),將會(huì)發(fā)生什么?
 11-5. read命令的多行輸入
 11-6. 檢測(cè)方向鍵
 11-7. 通過文件重定向來使用read
 11-8. 管道輸出到read中的問題
 11-9. 修改當(dāng)前的工作目錄
 11-10. 用"let"命令來作算術(shù)操作.
 11-11. 顯示eval命令的效果
 11-12. 強(qiáng)制登出(log-off)
 11-13. 另一個(gè)"rot13"的版本
 11-14. 在Perl腳本中使用eval命令來強(qiáng)制變量替換
 11-15. 使用set來改變腳本的位置參數(shù)
 11-16. 重新分配位置參數(shù)
 11-17. Unset一個(gè)變量
 11-18. 使用export命令傳遞一個(gè)變量到一個(gè)內(nèi)嵌awk的腳本中
 11-19. 使用getopts命令來讀取傳遞給腳本的選項(xiàng)/參數(shù).
 11-20. "Including"一個(gè)數(shù)據(jù)文件
 11-21. 一個(gè)沒什么用的,source自身的腳本
 11-22. exec的效果
 11-23. 一個(gè)exec自身的腳本
 11-24. 在繼續(xù)處理之前,等待一個(gè)進(jìn)程的結(jié)束
 11-25. 一個(gè)結(jié)束自身的腳本.
 12-1. 使用ls命令來創(chuàng)建一個(gè)燒錄CDR的內(nèi)容列表
 12-2. Hello or Good-bye
 12-3. 刪除當(dāng)前目錄下文件名中包含一些特殊字符(包括空白)的文件..
 12-4. 通過文件的 inode 號(hào)來刪除文件
 12-5. Logfile: 使用 xargs 來監(jiān)控系統(tǒng) log
 12-6. 把當(dāng)前目錄下的文件拷貝到另一個(gè)文件中
 12-7. 通過名字Kill進(jìn)程
 12-8. 使用xargs分析單詞出現(xiàn)的頻率
 12-9. 使用 expr
 12-10. 使用 date 命令
 12-11. 分析單詞出現(xiàn)的頻率
 12-12. 那個(gè)文件是腳本?
 12-13. 產(chǎn)生10進(jìn)制隨機(jī)數(shù)
 12-14. 使用 tail 命令來監(jiān)控系統(tǒng)log
 12-15. 在一個(gè)腳本中模仿 "grep" 的行為
 12-16. 在1913年的韋氏詞典中查找定義
 12-17. 檢查列表中單詞的正確性
 12-18. 轉(zhuǎn)換大寫: 把一個(gè)文件的內(nèi)容全部轉(zhuǎn)換為大寫.
 12-19. 轉(zhuǎn)換小寫: 將當(dāng)前目錄下的所有文全部轉(zhuǎn)換為小寫.
 12-20. Du: DOS 到 UNIX 文本文件的轉(zhuǎn)換.
 12-21. rot13: rot13, 弱智加密.
 12-22. Generating "Crypto-Quote" Puzzles
 12-23. 格式化文件列表.
 12-24. 使用 column 來格式化目錄列表
 12-25. nl: 一個(gè)自己計(jì)算行號(hào)的腳本.
 12-26. manview: 查看格式化的man頁
 12-27. 使用 cpio 來拷貝一個(gè)目錄樹
 12-28. 解包一個(gè) rpm 歸檔文件
 12-29. 從 C 文件中去掉注釋
 12-30. Exploring /usr/X11R6/bin
 12-31. 一個(gè)"改進(jìn)過"的 strings 命令
 12-32. 在一個(gè)腳本中使用 cmp 來比較2個(gè)文件.
 12-33. basename 和 dirname
 12-34. 檢查文件完整性
 12-35. Uudecod 編碼后的文件
 12-36. 查找濫用的連接來報(bào)告垃圾郵件發(fā)送者
 12-37. 分析一個(gè)垃圾郵件域
 12-38. 獲得一份股票報(bào)價(jià)
 12-39. 更新 Fedora Core 4
 12-40. 使用 ssh
 12-41. 一個(gè)可以mail自己的腳本
 12-42. 按月償還貸款
 12-43. 數(shù)制轉(zhuǎn)換
 12-44. 使用 "here document" 來調(diào)用 bc
 12-45. 計(jì)算圓周率
 12-46. 將10進(jìn)制數(shù)字轉(zhuǎn)換為16進(jìn)制數(shù)字
 12-47. 因子分解
 12-48. 計(jì)算直角三角形的斜邊
 12-49. 使用 seq 來產(chǎn)生循環(huán)參數(shù)
 12-50. 字母統(tǒng)計(jì)
 12-51. 使用getopt來分析命令行選項(xiàng)
 12-52. 一個(gè)拷貝自身的腳本
 12-53. 練習(xí)dd
 12-54. 記錄按鍵
 12-55. 安全的刪除一個(gè)文件
 12-56. 文件名產(chǎn)生器
 12-57. 將米轉(zhuǎn)換為英里
 12-58. 使用 m4
 13-1. 設(shè)置一個(gè)新密碼
 13-2. 設(shè)置一個(gè)擦除字符
 13-3. 關(guān)掉終端對(duì)于密碼的echo
 13-4. 按鍵檢測(cè)
 13-5. Checking a remote server for identd
 13-6. pidof 幫助殺掉一個(gè)進(jìn)程
 13-7. 檢查一個(gè)CD鏡像
 13-8. 在一個(gè)文件中創(chuàng)建文件系統(tǒng)
 13-9. 添加一個(gè)新的硬盤驅(qū)動(dòng)器
 13-10. 使用umask來將輸出文件隱藏起來
 13-11. killall, 來自于 /etc/rc.d/init.d
 14-1. 愚蠢的腳本策略
 14-2. 從循環(huán)的輸出中產(chǎn)生一個(gè)變量
 14-3. 找anagram(回文構(gòu)詞法, 可以將一個(gè)有意義的單詞, 變換為1個(gè)或多個(gè)有意義的單詞, 但是還是原來的子母集合)
 16-1. 使用exec重定向標(biāo)準(zhǔn)輸入
 16-2. 使用exec來重定向stdout
 16-3. 使用exec在同一腳本中重定向stdin和stdout
 16-4. 避免子shell
 16-5. while循環(huán)的重定向
 16-6. 另一種while循環(huán)的重定向
 16-7. until循環(huán)重定向
 16-8. for循環(huán)重定向
 16-9. for循環(huán)重定向 loop (將標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出都重定向了)
 16-10. 重定向if/then測(cè)試結(jié)構(gòu)
 16-11. 用于上面例子的"names.data"數(shù)據(jù)文件
 16-12. 記錄日志事件
 17-1. 廣播: 發(fā)送消息給每個(gè)登錄上的用戶
 17-2. 仿造文件: 創(chuàng)建一個(gè)兩行的仿造文件
 17-3. 使用cat的多行消息
 17-4. 帶有抑制tab功能的多行消息
 17-5. 使用參數(shù)替換的here document
 17-6. 上傳一個(gè)文件對(duì)到"Sunsite"的incoming目錄
 17-7. 關(guān)閉參數(shù)替換
 17-8. 一個(gè)產(chǎn)生另外一個(gè)腳本的腳本
 17-9. Here documents與函數(shù)
 17-10. "匿名" here Document
 17-11. 注釋掉一段代碼塊
 17-12. 一個(gè)自文檔化(self-documenting)的腳本
 17-13. 在一個(gè)文件的開頭添加文本
 20-1. 子shell中的變量作用域
 20-2. 列出用戶的配置文件
 20-3. 在子shell里進(jìn)行串行處理
 21-1. 在受限的情況下運(yùn)行腳本
 23-1. 簡單函數(shù)
 23-2. 帶著參數(shù)的函數(shù)
 23-3. 函數(shù)和被傳給腳本的命令行參數(shù)
 23-4. 傳遞間接引用給函數(shù)
 23-5. 解除傳遞給函數(shù)的參數(shù)引用
 23-6. 再次嘗試解除傳遞給函數(shù)的參數(shù)引用
 23-7. 兩個(gè)數(shù)中的最大者
 23-8. 把數(shù)字轉(zhuǎn)化成羅馬數(shù)字
 23-9. 測(cè)試函數(shù)最大的返回值
 23-10. 比較兩個(gè)大整數(shù)
 23-11. 用戶名的真實(shí)名
 23-12. 局部變量的可見范圍
 23-13. 用局部變量來遞歸
 23-14. 漢諾塔
 24-1. 腳本中的別名
 24-2. unalias: 設(shè)置和刪除別名
 25-1. 使用"與列表(and list)"來測(cè)試命令行參數(shù)
 25-2. 用"與列表"的另一個(gè)命令行參數(shù)測(cè)試
 25-3. "或列表"和"與列表"的結(jié)合使用
 26-1. 簡單的數(shù)組用法
 26-2. 格式化一首詩
 26-3. 多種數(shù)組操作
 26-4. 用于數(shù)組的字符串操作符
 26-5. 將腳本的內(nèi)容傳給數(shù)組
 26-6. 一些數(shù)組專用的工具
 26-7. 關(guān)于空數(shù)組和空數(shù)組元素
 26-8. 初始化數(shù)組
 26-9. 復(fù)制和連接數(shù)組
 26-10. 關(guān)于連接數(shù)組的更多信息
 26-11. 一位老朋友: 冒泡排序
 26-12. 內(nèi)嵌數(shù)組和間接引用
 26-13. 復(fù)雜數(shù)組應(yīng)用: 埃拉托色尼素?cái)?shù)篩子
 26-14. 模擬下推的堆棧
 26-15. 復(fù)雜的數(shù)組應(yīng)用: 列出一種怪異的數(shù)學(xué)序列
 26-16. 模擬二維數(shù)組,并使它傾斜
 27-1. 利用/dev/tcp 來檢修故障
 27-2. 搜索與一個(gè)PID相關(guān)的進(jìn)程
 27-3. 網(wǎng)絡(luò)連接狀態(tài)
 28-1. 隱藏cookie而不再使用
 28-2. 用/dev/zero創(chuàng)建一個(gè)交換臨時(shí)文件
 28-3. 創(chuàng)建ramdisk
 29-1. 一個(gè)錯(cuò)誤的腳本
 29-2. 丟失關(guān)鍵字(keyword)
 29-3. 另一個(gè)錯(cuò)誤腳本
 29-4. 用"assert"測(cè)試條件
 29-5. 捕捉 exit
 29-6. 在Control-C后清除垃圾
 29-7. 跟蹤變量
 29-8. 運(yùn)行多進(jìn)程 (在多處理器的機(jī)器里)
 31-1. 數(shù)字和字符串比較是不相等同的
 31-2. 子SHELL缺陷
 31-3. 把echo的輸出用管道輸送給read命令
 33-1. shell 包裝
 33-2. 稍微復(fù)雜一些的shell包裝
 33-3. 寫到日志文件的shell包裝
 33-4. 包裝awk的腳本
 33-5. 另一個(gè)包裝awk的腳本
 33-6. 把Perl嵌入Bash腳本
 33-7. Bash 和 Perl 腳本聯(lián)合使用
 33-8. 遞歸調(diào)用自己本身的(無用)腳本
 33-9. 遞歸調(diào)用自己本身的(有用)腳本
 33-10. 另一個(gè)遞歸調(diào)用自己本身的(有用)腳本
 33-11. 一個(gè) "彩色的" 地址資料庫
 33-12. 畫盒子
 33-13. 顯示彩色文本
 33-14. "賽馬" 游戲
 33-15. 返回值技巧
 33-16. 整型還是string?
 33-17. 傳遞和返回?cái)?shù)組
 33-18. anagrams游戲
 33-19. 在shell腳本中調(diào)用的窗口部件
 34-1. 字符串?dāng)U展
 34-2. 間接變量引用 - 新方法
 34-3. 使用間接變量引用的簡單數(shù)據(jù)庫應(yīng)用
 34-4. 用數(shù)組和其他的小技巧來處理四人隨機(jī)打牌
 
 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
 
 第一部分 熱身
 ++++++++++++++++
 shell是一個(gè)命令解釋器.是介于操作系統(tǒng)kernel與用戶之間的一個(gè)絕緣層.準(zhǔn)確地說,它也是一
 一種強(qiáng)力的計(jì)算機(jī)語言.一個(gè)shell程序,被稱為一個(gè)腳本,是一種很容易使用的工具,它可以通過
 將系統(tǒng)調(diào)用,公共程序,工具,和編譯過的二進(jìn)制程序粘合在一起來建立應(yīng)用.事實(shí)上,所有的UNIX
 命令和工具再加上公共程序,對(duì)于shell腳本來說,都是可調(diào)用的.如果這些你還覺得不夠,那么
 shell內(nèi)建命令,比如test與循環(huán)結(jié)構(gòu),也會(huì)給腳本添加強(qiáng)力的支持和增加靈活性.Shell腳本對(duì)于
 管理系統(tǒng)任務(wù)和其它的重復(fù)工作的例程來說,表現(xiàn)的非常好,根本不需要那些華而不實(shí)的成熟
 緊湊的程序語言.
 
 第1章 為什么使用shell編程
 ===========================
 沒有程序語言是完美的.甚至沒有一個(gè)唯一最好的語言,只有對(duì)于特定目的,比較適合和不適合
 的程序語言.
 Herbert Mayer
 
 對(duì)于任何想適當(dāng)精通一些系統(tǒng)管理知識(shí)的人來說,掌握shell腳本知識(shí)都是最基本的,即使這些
 人可能并不打算真正的編寫一些腳本.想一下Linux機(jī)器的啟動(dòng)過程,在這個(gè)過程中,必將運(yùn)行
 /etc/rc.d目錄下的腳本來存儲(chǔ)系統(tǒng)配置和建立服務(wù).詳細(xì)的理解這些啟動(dòng)腳本對(duì)于分析系統(tǒng)的
 行為是非常重要的,并且有時(shí)候可能必須修改它.
 
 學(xué)習(xí)如何編寫shell腳本并不是一件很困難的事,因?yàn)槟_本可以分為很小的塊,并且相對(duì)于shell
 特性的操作和選項(xiàng)[1]部分,只需要學(xué)習(xí)很小的一部分就可以了.語法是簡單并且直觀的,編寫腳
 本很像是在命令行上把一些相關(guān)命令和工具連接起來,并且只有很少的一部分規(guī)則需要學(xué)習(xí).
 絕大部分腳本第一次就可以正常的工作,而且即使調(diào)試一個(gè)長一些的腳本也是很直觀的.
 
 一個(gè)shell腳本是一個(gè)類似于小吃店的(quick and dirty)方法,在你使用原型設(shè)計(jì)一個(gè)復(fù)雜的
 應(yīng)用的時(shí)候.在工程開發(fā)的第一階段,即使從功能中取得很有限的一個(gè)子集放到shell腳本中來
 完成往往都是非常有用的.使用這種方法,程序的結(jié)果可以被測(cè)試和嘗試運(yùn)行,并且在處理使用
 諸如C/C++,Java或者Perl語言編寫的最終代碼前,主要的缺陷和陷阱往往就被發(fā)現(xiàn)了.
 
 Shell腳本遵循典型的UNIX哲學(xué),就是把大的復(fù)雜的工程分成小規(guī)模的子任務(wù),并且把這些部件
 和工具組合起來.許多人認(rèn)為這種辦法更好一些,至少這種辦法比使用那種高\(yùn)大\全的語言更
 美,更愉悅,更適合解決問題.比如Perl就是這種能干任何事能適合任何人的語言,但是代價(jià)就是
 你需要強(qiáng)迫自己使用這種語言來思考解決問題的辦法.
 
 什么時(shí)候不使用Shell腳本
 
 資源密集型的任務(wù),尤其在需要考慮效率時(shí)(比如,排序,hash等等)
 
 需要處理大任務(wù)的數(shù)學(xué)操作,尤其是浮點(diǎn)運(yùn)算,精確運(yùn)算,或者復(fù)雜的算術(shù)運(yùn)算
 (這種情況一般使用C++或FORTRAN來處理)
 
 有跨平臺(tái)移植需求(一般使用C或Java)
 
 復(fù)雜的應(yīng)用,在必須使用結(jié)構(gòu)化編程的時(shí)候(需要變量的類型檢查,函數(shù)原型,等等)
 
 對(duì)于影響系統(tǒng)全局性的關(guān)鍵任務(wù)應(yīng)用。
 
 對(duì)于安全有很高要求的任務(wù),比如你需要一個(gè)健壯的系統(tǒng)來防止入侵,破解,惡意破壞等等.
 
 項(xiàng)目由連串的依賴的各個(gè)部分組成。
 
 需要大規(guī)模的文件操作
 
 需要多維數(shù)組的支持
 
 需要數(shù)據(jù)結(jié)構(gòu)的支持,比如鏈表或數(shù)等數(shù)據(jù)結(jié)構(gòu)
 
 需要產(chǎn)生或操作圖形化界面GUI
 
 需要直接操作系統(tǒng)硬件
 
 需要I/O或socket接口
 
 需要使用庫或者遺留下來的老代碼的接口
 
 私人的,閉源的應(yīng)用(shell腳本把代碼就放在文本文件中,全世界都能看到)
 
 如果你的應(yīng)用符合上邊的任意一條,那么就考慮一下更強(qiáng)大的語言吧--或許是Perl,Tcl,Python,
 Ruby -- 或者是更高層次的編譯語言比如C/C++,或者是Java.即使如此,你會(huì)發(fā)現(xiàn),使用shell
 來原型開發(fā)你的應(yīng)用,在開發(fā)步驟中也是非常有用的.
 
 我們將開始使用Bash,Bash是"Bourne-Again shell"首字母的縮寫,也是Stephen Bourne的經(jīng)典
 的Bourne shell的一個(gè)雙關(guān)語,(譯者:說實(shí)話,我一直搞不清這個(gè)雙關(guān)語是什么意思,為什么叫
 "Bourn-Again shell",這其中應(yīng)該有個(gè)什么典故吧,哪位好心,告訴我一下^^).Bash已經(jīng)成為了
 所有UNIX中shell腳本的事實(shí)上的標(biāo)準(zhǔn)了.同時(shí)這本書也覆蓋了絕大部分的其他一些shell的原
 則,比如Korn Shell,Bash從ksh中繼承了一部分特性,[2]C Shell和它的變種.(注意:C Shell
 編程是不被推薦的,因?yàn)橐恍┨囟ǖ膬?nèi)在問題,Tom Christiansen在1993年10月指出了這個(gè)問題
 請(qǐng)?jiān)趆ttp://www.etext.org/Quartz/computer/unix/csh.harmful.gz中查看具體內(nèi)容.)
 
 接下來是腳本的一些說明.在展示shell不同的特征之前,它可以減輕一些閱讀書中例子
 的負(fù)擔(dān).本書中的例子腳本,都在盡可能的范圍內(nèi)進(jìn)行了測(cè)試,并且其中的一些將使用在真
 實(shí)的生活中.讀者可以運(yùn)行這些例子腳本(使用scriptname.sh或者scriptname.bash的形式),
 [3]并給這些腳本執(zhí)行權(quán)限(chmod u+rx scriptname),然后執(zhí)行它們,看看發(fā)生了什么.如果存
 檔的腳本不可用,那么就從本書的HTML,pdf或者text的發(fā)行版本中把它們拷貝粘貼出來.考慮到
 這些腳本中的內(nèi)容在我們還沒解釋它之前就被列在這里,可能會(huì)影響讀者的理解,這就需要讀者
 暫時(shí)忽略這些內(nèi)容.
 
 除非特別注明,本書作者編寫了本書中的絕大部分例子腳本.
 
 注意事項(xiàng):
 [1] 這些在builtins章節(jié)被引用,這些是shell的內(nèi)部特征.
 [2] ksh88的許多特性,甚至是一些ksh93的特性都被合并到Bash中了.
 [3] 根據(jù)慣例,用戶編寫的Bourne shell腳本應(yīng)該在腳本的名字后邊加上.sh擴(kuò)展名.
 一些系統(tǒng)腳本,比如那些在/etc/rc.d中的腳本,則不遵循這種命名習(xí)慣.
 
 
 
 第2章 帶著一個(gè)Sha-Bang出發(fā)(Sha-Bang指的是#!)
 ==============================================
 在一個(gè)最簡單的例子中,一個(gè)shell腳本其實(shí)就是將一堆系統(tǒng)命令列在一個(gè)文件中.它的最基本的
 用處就是,在你每次輸入這些特定順序的命令時(shí)可以少敲一些字.
 
 Example 2-1 清除:清除/var/log下的log文件
 ################################Start Script#######################################
 1 # Cleanup
 2 # 當(dāng)然要使用root身份來運(yùn)行這個(gè)腳本
 3
 4 cd /var/log
 5 cat /dev/null > messages
 6 cat /dev/null > wtmp
 7 echo "Logs cleaned up."
 ################################End Script#########################################
 這根本就沒什么稀奇的, 只不過是命令的堆積, 來讓從console或者xterm中一個(gè)一個(gè)的輸入命
 令更方便一些.好處就是把所有命令都放在一個(gè)腳本中,不用每次都敲它們.這樣的話,對(duì)于特定
 的應(yīng)用來說,這個(gè)腳本就很容易被修改或定制.
 
 Example 2-2 清除:一個(gè)改良的清除腳本
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # 一個(gè)Bash腳本的正確的開頭部分.
 3
 4 # Cleanup, 版本 2
 5
 6 # 當(dāng)然要使用root身份來運(yùn)行.
 7 # 在此處插入代碼,來打印錯(cuò)誤消息,并且在不是root身份的時(shí)候退出.
 8
 9 LOG_DIR=/var/log
 10 # 如果使用變量,當(dāng)然比把代碼寫死的好.
 11 cd $LOG_DIR
 12
 13 cat /dev/null > messages
 14 cat /dev/null > wtmp
 15
 16
 17 echo "Logs cleaned up."
 18
 19 exit # 這個(gè)命令是一種正確并且合適的退出腳本的方法.
 ################################End Script#########################################
 
 現(xiàn)在,讓我們看一下一個(gè)真正意義的腳本.而且我們可以走得更遠(yuǎn)...
 Example 2-3. cleanup:一個(gè)增強(qiáng)的和廣義的刪除logfile的腳本
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # 清除, 版本 3
 3
 4 # Warning:
 5 # -------
 6 # 這個(gè)腳本有好多特征,這些特征是在后邊章節(jié)進(jìn)行解釋的,大概是進(jìn)行到本書的一半的
 7 # 時(shí)候,
 8 # 你就會(huì)覺得它沒有什么神秘的了.
 9 #
 10
 11
 12
 13 LOG_DIR=/var/log
 14 ROOT_UID=0 # $UID為0的時(shí)候,用戶才具有根用戶的權(quán)限
 15 LINES=50 # 默認(rèn)的保存行數(shù)
 16 E_XCD=66 # 不能修改目錄?
 17 E_NOTROOT=67 # 非根用戶將以error退出
 18
 19
 20 # 當(dāng)然要使用根用戶來運(yùn)行
 21 if [ "$UID" -ne "$ROOT_UID" ]
 22 then
 23 echo "Must be root to run this script."
 24 exit $E_NOTROOT
 25 fi
 26
 27 if [ -n "$1" ]
 28 # 測(cè)試是否有命令行參數(shù)(非空).
 29 then
 30 lines=$1
 31 else
 32 lines=$LINES # 默認(rèn),如果不在命令行中指定
 33 fi
 34
 35
 36 # Stephane Chazelas 建議使用下邊
 37 #+ 的更好方法來檢測(cè)命令行參數(shù).
 38 #+ 但對(duì)于這章來說還是有點(diǎn)超前.
 39 #
 40 # E_WRONGARGS=65 # 非數(shù)值參數(shù)(錯(cuò)誤的參數(shù)格式)
 41 #
 42 # case "$1" in
 43 # "" ) lines=50;;
 44 # *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
 45 # * ) lines=$1;;
 46 # esac
 47 #
 48 #* 直到"Loops"的章節(jié)才會(huì)對(duì)上邊的內(nèi)容進(jìn)行詳細(xì)的描述.
 49
 50
 51 cd $LOG_DIR
 52
 53 if [ `pwd` != "$LOG_DIR" ] # 或者 if[ "$PWD" != "$LOG_DIR" ]
 54 # 不在 /var/log中?
 55 then
 56 echo "Can't change to $LOG_DIR."
 57 exit $E_XCD
 58 fi # 在處理log file之前,再確認(rèn)一遍當(dāng)前目錄是否正確.
 59
 60 # 更有效率的做法是
 61 #
 62 # cd /var/log || {
 63 # echo "Cannot change to necessary directory." >&2
 64 # exit $E_XCD;
 65 # }
 66
 67
 68
 69
 70 tail -$lines messages > mesg.temp # 保存log file消息的最后部分.
 71 mv mesg.temp messages # 變?yōu)樾碌膌og目錄.
 72
 73
 74 # cat /dev/null > messages
 75 #* 不再需要了,使用上邊的方法更安全.
 76
 77 cat /dev/null > wtmp # ': > wtmp' 和 '> wtmp'具有相同的作用
 78 echo "Logs cleaned up."
 79
 80 exit 0
 81 # 退出之前返回0,返回0表示成功.
 82 #
 ################################End Script#########################################
 
 因?yàn)槟憧赡芟M麑⑾到y(tǒng)log全部消滅,這個(gè)版本留下了log消息最后的部分.你將不斷地找到新
 的方法來完善這個(gè)腳本,并提高效率.
 
 要注意,在每個(gè)腳本的開頭都使用"#!",這意味著告訴你的系統(tǒng)這個(gè)文件的執(zhí)行需要指定一個(gè)解
 釋器.#!實(shí)際上是一個(gè)2字節(jié)[1]的魔法數(shù)字,這是指定一個(gè)文件類型的特殊標(biāo)記, 換句話說, 在
 這種情況下,指的就是一個(gè)可執(zhí)行的腳本(鍵入man magic來獲得關(guān)于這個(gè)迷人話題的更多詳細(xì)
 信息).在#!之后接著是一個(gè)路徑名.這個(gè)路徑名指定了一個(gè)解釋腳本中命令的程序,這個(gè)程序可
 以是shell,程序語言或者是任意一個(gè)通用程序.這個(gè)指定的程序從頭開始解釋并且執(zhí)行腳本中
 的命令(從#!行下邊的一行開始),忽略注釋.[2]
 如:
 1 #!/bin/sh
 2 #!/bin/bash
 3 #!/usr/bin/perl
 4 #!/usr/bin/tcl
 5 #!/bin/sed -f
 6 #!/usr/awk -f
 
 上邊每一個(gè)腳本頭的行都指定了一個(gè)不同的命令解釋器,如果是/bin/sh,那么就是默認(rèn)shell
 (在Linux系統(tǒng)中默認(rèn)是Bash).[3]使用#!/bin/sh,在大多數(shù)商業(yè)發(fā)行的UNIX上,默認(rèn)是Bourne
 shell,這將讓你的腳本可以正常的運(yùn)行在非Linux機(jī)器上,雖然這將會(huì)犧牲Bash一些獨(dú)特的特征.
 腳本將與POSIX[4] 的sh標(biāo)準(zhǔn)相一致.
 
 注意: #! 后邊給出的路徑名必須是正確的,否則將會(huì)出現(xiàn)一個(gè)錯(cuò)誤消息,通常是
 "Command not found",這將是你運(yùn)行這個(gè)腳本時(shí)所得到的唯一結(jié)果.
 
 當(dāng)然"#!"也可以被忽略,不過這樣你的腳本文件就只能是一些命令的集合,不能夠使用shell內(nèi)建
 的指令了,如果不能使用變量的話,當(dāng)然這也就失去了腳本編程的意義了.
 
 注意:這個(gè)例子鼓勵(lì)你使用模塊化的方式來編寫腳本,平時(shí)也要注意收集一些零碎的代碼,
 這些零碎的代碼可能用在你將來編寫的腳本中.這樣你就可以通過這些代碼片段來構(gòu)
 造一個(gè)較大的工程用例. 以下邊腳本作為序,來測(cè)試腳本被調(diào)用的參數(shù)是否正確.
 ################################Start Script#######################################
 1 E_WRONG_ARGS=65
 2 script_parameters="-a -h -m -z"
 3 # -a = all, -h = help, 等等.
 4
 5 if [ $# -ne $Number_of_expected_args ]
 6 then
 7 echo "Usage: `basename $0` $script_parameters"
 8 # `basename $0`是這個(gè)腳本的文件名
 9 exit $E_WRONG_ARGS
 10 fi
 ################################End Script#########################################
 大多數(shù)情況下,你需要編寫一個(gè)腳本來執(zhí)行一個(gè)特定的任務(wù),在本章中第一個(gè)腳本就是一個(gè)這樣
 的例子, 然后你會(huì)修改它來完成一個(gè)不同的,但比較相似的任務(wù).用變量來代替寫死的常量,就是
 一個(gè)好方法,將重復(fù)的代碼放到一個(gè)函數(shù)中,也是一種好習(xí)慣.
 
 
 2.1 調(diào)用一個(gè)腳本
 ----------------
 編寫完腳本之后,你可以使用sh scriptname,[5]或者bash scriptname來調(diào)用它.
 (不推薦使用sh <SCRIPTNAME,因?yàn)檫@禁用了腳本從STDIN中讀數(shù)據(jù)的功能.)
 更方便的方法是讓腳本本身就具有可執(zhí)行權(quán)限,通過chmod命令可以修改.
 
 比如:
 chmod 555 scriptname (允許任何人都具有 可讀和執(zhí)行權(quán)限) [6]
 或:
 chmod +rx scriptname (允許任何人都具有 可讀和執(zhí)行權(quán)限)
 chmod u+rx scriptname (只給腳本的所有者 可讀和執(zhí)行權(quán)限)
 
 既然腳本已經(jīng)具有了可執(zhí)行權(quán)限,現(xiàn)在你可以使用./scriptname.[7]來測(cè)試它了.如果這個(gè)腳本
 以一個(gè)"#!"行開頭,那么腳本將會(huì)調(diào)用合適的命令解釋器來運(yùn)行.
 
 最后一步,在腳本被測(cè)試和debug之后,你可能想把它移動(dòng)到/usr/local/bin(當(dāng)然是以root身份)
 ,來讓你的腳本對(duì)所有用戶都有用.這樣用戶就可以直接敲腳本名字來運(yùn)行了.
 
 注意事項(xiàng):
 [1] 那些具有UNIX味道的腳本(基于4.2BSD)需要一個(gè)4字節(jié)的魔法數(shù)字,在#!后邊需要一個(gè)
 空格#! /bin/sh.
 [2] 腳本中的#!行的最重要的任務(wù)就是命令解釋器(sh或者bash).因?yàn)檫@行是以#開始的,
 當(dāng)命令解釋器執(zhí)行這個(gè)腳本的時(shí)候,會(huì)把它作為一個(gè)注釋行.當(dāng)然,在這之前,這行語句
 已經(jīng)完成了它的任務(wù),就是調(diào)用命令解釋器.
 
 如果在腳本的里邊還有一個(gè)#!行,那么bash將把它認(rèn)為是一個(gè)一般的注釋行.
 1 #!/bin/bash
 2
 3 echo "Part 1 of script."
 4 a=1
 5
 6 #!/bin/bash
 7 # 這將不會(huì)開始一個(gè)新腳本.
 8
 9 echo "Part 2 of script."
 10 echo $a # Value of $a stays at 1.
 [3] 這里可以玩一些小技巧.
 1 #!/bin/rm
 2 # 自刪除腳本.
 3
 4 # 當(dāng)你運(yùn)行這個(gè)腳本時(shí),基本上什么都不會(huì)發(fā)生...除非這個(gè)文件消失不見.
 5
 6 WHATEVER=65
 7
 8 echo "This line will never print (betcha!)."
 9
 10 exit $WHATEVER # 沒關(guān)系,腳本是不會(huì)在這退出的.
 當(dāng)然,你還可以試試在一個(gè)README文件的開頭加上#!/bin/more,并讓它具有執(zhí)行權(quán)限.
 結(jié)果將是文檔自動(dòng)列出自己的內(nèi)容.(一個(gè)使用cat命令的here document可能是一個(gè)
 更好的選則,--見Example 17-3).
 [4] 可移植的操作系統(tǒng)接口,標(biāo)準(zhǔn)化類UNIX操作系統(tǒng)的一種嘗試.POSIX規(guī)范可以在
 http://www.opengroup.org/onlinepubs/007904975/toc.htm中查閱.
 [5] 小心:使用sh scriptname來調(diào)用腳本的時(shí)候?qū)?huì)關(guān)閉一些Bash特定的擴(kuò)展,腳本可能
 因此而調(diào)用失敗.
 [6] 腳本需要讀和執(zhí)行權(quán)限,因?yàn)閟hell需要讀這個(gè)腳本.
 [7] 為什么不直接使用scriptname來調(diào)用腳本?如果你當(dāng)前的目錄下($PWD)正好有你想要
 執(zhí)行的腳本,為什么它運(yùn)行不了呢?失敗的原因是,出于安全考慮,當(dāng)前目錄并沒有被
 加在用戶的$PATH變量中.因此,在當(dāng)前目錄下調(diào)用腳本必須使用./scriptname這種
 形式.
 
 
 2.2 初步的練習(xí)
 --------------
 1. 系統(tǒng)管理員經(jīng)常會(huì)為了自動(dòng)化一些常用的任務(wù)而編寫腳本.舉出幾個(gè)這種有用的腳本的實(shí)例.
 2. 編寫一個(gè)腳本,顯示時(shí)間和日期,列出所有的登錄用戶,顯示系統(tǒng)的更新時(shí)間.然后這個(gè)腳本
 將會(huì)把這些內(nèi)容保存到一個(gè)log file中.
 
 
 第二部分 基本
 ++++++++++++++++
 
 第3章 特殊字符
 ================
 
 # 注釋,行首以#開頭為注釋(#!是個(gè)例外).
 
 1 # This line is a comment.
 
 注釋也可以存在于本行命令的后邊.
 
 1 echo "A comment will follow." # 注釋在這里
 2 # ^ 注意#前邊的空白
 
 注釋也可以在本行空白的后邊.
 
 1 # A tab precedes this comment.
 
 注意:命令是不能跟在同一行上注釋的后邊的,沒有辦法,在同一行上,注釋的后邊想
 要再使用命令,只能另起一行.
 當(dāng)然,在echo命令中被轉(zhuǎn)義的#是不能作為注釋的.
 同樣的,#也可以出現(xiàn)在特定的參數(shù)替換結(jié)構(gòu)中或者是數(shù)字常量表達(dá)式中.
 
 1 echo "The # here does not begin a comment."
 2 echo 'The # here does not begin a comment.'
 3 echo The \# here does not begin a comment.
 4 echo The # 這里開始一個(gè)注釋
 5
 6 echo ${PATH#*:} # 參數(shù)替換,不是一個(gè)注釋
 7 echo $(( 2#101011 )) # 數(shù)制轉(zhuǎn)換,不是一個(gè)注釋
 8
 9 # Thanks, S.C.
 
 標(biāo)準(zhǔn)的引用和轉(zhuǎn)義字符("'\)可以用來轉(zhuǎn)義#
 
 ; 命令分隔符,可以用來在一行中來寫多個(gè)命令.
 
 1 echo hello; echo there
 2
 3
 4 if [ -x "$filename" ]; then # 注意:"if"和"then"需要分隔
 5 # 為啥?
 6 echo "File $filename exists."; cp $filename $filename.bak
 7 else
 8 echo "File $filename not found."; touch $filename
 9 fi; echo "File test complete."
 
 有時(shí)候需要轉(zhuǎn)義
 
 ;; 終止"case"選項(xiàng).
 
 1 case "$variable" in
 2 abc) echo "\$variable = abc" ;;
 3 xyz) echo "\$variable = xyz" ;;
 4 esac
 
 . .命令等價(jià)于source命令(見Example 11-20).這是一個(gè)bash的內(nèi)建命令.
 
 . .作為文件名的一部分.如果作為文件名的前綴的話,那么這個(gè)文件將成為隱藏文件.
 將不被ls命令列出.
 
 bash$ touch .hidden-file
 bash$ ls -l
 total 10
 -rw-r--r-- 1 bozo 4034 Jul 18 22:04 data1.addressbook
 -rw-r--r-- 1 bozo 4602 May 25 13:58 data1.addressbook.bak
 -rw-r--r-- 1 bozo 877 Dec 17 2000 employment.addressbook
 
 
 bash$ ls -al
 total 14
 drwxrwxr-x 2 bozo bozo 1024 Aug 29 20:54 ./
 drwx------ 52 bozo bozo 3072 Aug 29 20:51 ../
 -rw-r--r-- 1 bozo bozo 4034 Jul 18 22:04 data1.addressbook
 -rw-r--r-- 1 bozo bozo 4602 May 25 13:58 data1.addressbook.bak
 -rw-r--r-- 1 bozo bozo 877 Dec 17 2000 employment.addressbook
 -rw-rw-r-- 1 bozo bozo 0 Aug 29 20:54 .hidden-file
 
 .命令如果作為目錄名的一部分的話,那么.表達(dá)的是當(dāng)前目錄.".."表示上一級(jí)目錄.
 
 bash$ pwd
 /home/bozo/projects
 
 bash$ cd .
 bash$ pwd
 /home/bozo/projects
 
 bash$ cd ..
 bash$ pwd
 /home/bozo/
 
 .命令經(jīng)常作為一個(gè)文件移動(dòng)命令的目的地.
 
 bash$ cp /home/bozo/current_work/junk/* .
 
 . .字符匹配,這是作為正則表達(dá)是的一部分,用來匹配任何的單個(gè)字符.
 
 " 部分引用."STRING"阻止了一部分特殊字符,具體見第5章.
 
 ' 全引用. 'STRING' 阻止了全部特殊字符,具體見第5章.
 
 , 逗號(hào)鏈接了一系列的算術(shù)操作,雖然里邊所有的內(nèi)容都被運(yùn)行了,但只有最后一項(xiàng)被
 返回.
 
 如:
 1 let "t2 = ((a = 9, 15 / 3))" # Set "a = 9" and "t2 = 15 / 3"
 
 \ 轉(zhuǎn)義字符,如\X等價(jià)于"X"或'X',具體見第5章.
 
 / 文件名路徑分隔符.或用來做除法操作.
 
 ` 后置引用,命令替換,具體見第14章
 
 : 空命令,等價(jià)于"NOP"(no op,一個(gè)什么也不干的命令).也可以被認(rèn)為與shell的內(nèi)建命令(true)作用相同.":"命令是一
 個(gè)bash的內(nèi)建命令,它的返回值為0,就是shell返回的true.
 
 如:
 1 :
 2 echo $? # 0
 
 死循環(huán),如:
 
 1 while :
 2 do
 3 operation-1
 4 operation-2
 5 ...
 6 operation-n
 7 done
 8
 9 # 與下邊相同:
 10 # while true
 11 # do
 12 # ...
 13 # done
 
 在if/then中的占位符,如:
 1 if condition
 2 then : # 什么都不做,引出分支.
 3 else
 4 take-some-action
 5 fi
 
 在一個(gè)2元命令中提供一個(gè)占位符,具體見Example 8-2,和"默認(rèn)參數(shù)".如:
 1 : ${username=`whoami`}
 2 # ${username=`whoami`} 如果沒有":"的話,將給出一個(gè)錯(cuò)誤,除非"username"是
 3 # 個(gè)命令
 在here document中提供一個(gè)占位符,見Example 17-10.
 
 使用"參數(shù)替換"來評(píng)估字符串變量(見Example 9-14).如:
 1 : ${HOSTNAME?} ${USER?} ${MAIL?}
 2 # 如果一個(gè)或多個(gè)必要的環(huán)境變量沒被設(shè)置的話,
 3 #+ 就打印錯(cuò)誤信息.
 
 "變量擴(kuò)展/子串替換"
 在和 > (重定向操作符)結(jié)合使用時(shí),把一個(gè)文件截?cái)嗟?長度,沒有修改它的權(quán)限.
 如果文件在之前并不存在,那么就創(chuàng)建它.如:
 1 : > data.xxx #文件"data.xxx"現(xiàn)在被清空了.
 2
 3 #與 cat /dev/null >data.xxx 的作用相同
 4 #然而,這不會(huì)產(chǎn)生一個(gè)新的進(jìn)程,因?yàn)?#34;:"是一個(gè)內(nèi)建命令.
 具體參見Example 12-14.
 
 在和>>重定向操作符結(jié)合使用時(shí),將不會(huì)對(duì)想要附加的文件產(chǎn)生任何影響.
 如果文件不存在,將創(chuàng)建.
 注意: 這只適用于正規(guī)文件,而不是管道,符號(hào)連接,和某些特殊文件.
 
 也可能用來作為注釋行,雖然我們不推薦這么做.使用#來注釋的話,將關(guān)閉剩余行的
 錯(cuò)誤檢查,所以可以在注釋行中寫任何東西.然而,使用:的話將不會(huì)這樣.如:
 1 : This is a comment thar generates an error,(if [ $x -eq 3] ).
 
 ":"還用來在/etc/passwd和$PATH變量中用來做分隔符.
 bash$ echo $PATH
 /usr/local/bin:/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games
 ! 取反操作符,將反轉(zhuǎn)"退出狀態(tài)"結(jié)果,(見Example 6-2).也會(huì)反轉(zhuǎn)test操作符的意義.比
 如修改=為!=.!操作是Bash的一個(gè)關(guān)鍵字.
 
 在一個(gè)不同的上下文中,!也會(huì)出現(xiàn)在"間接變量引用"見Example 9-22.
 
 在另一種上下文中,!還能反轉(zhuǎn)bash的"history mechanism"(見附錄J 歷史命令)
 需要注意的是,在一個(gè)腳本中,"history mechanism"是被禁用的.
 
 * 萬能匹配字符,用于文件名匹配(這個(gè)東西有個(gè)專有名詞叫file globbing),或者是正則
 表達(dá)式中.注意:在正則表達(dá)式匹配中的作用和在文件名匹配中的作用是不同的.
 bash$ echo *
 abs-book.sgml add-drive.sh agram.sh alias.sh
 * 數(shù)學(xué)乘法.
 **是冪運(yùn)算.
 ? 測(cè)試操作.在一個(gè)確定的表達(dá)式中,用?來測(cè)試結(jié)果.
 (())結(jié)構(gòu)可以用來做數(shù)學(xué)計(jì)算或者是寫c代碼,那?就是c語言的3元操作符的
 一個(gè).
 在"參數(shù)替換"中,?測(cè)試一個(gè)變量是否被set了.
 ? 在file globbing中和在正則表達(dá)式中一樣匹配任意的單個(gè)字符.
 
 $ 變量替換
 1 var1=5
 2 var2=23skidoo
 3
 4 echo $var1 # 5
 5 echo $var2 # 23skidoo
 $ 在正則表達(dá)式中作為行結(jié)束符.
 ${} 參數(shù)替換,見9.3節(jié).
 $*,$@ 位置參數(shù)
 $? 退出狀態(tài)變量.$?保存一個(gè)命令/一個(gè)函數(shù)或者腳本本身的退出狀態(tài).
 $$ 進(jìn)程ID變量.這個(gè)$$變量保存運(yùn)行腳本進(jìn)程ID
 () 命令組.如:
 1 (a=hello;echo $a)
 注意:在()中的命令列表,將作為一個(gè)子shell來運(yùn)行.
 在()中的變量,由于是在子shell中,所以對(duì)于腳本剩下的部分是不可用的.
 如:
 1 a=123
 2 ( a=321; )
 3
 4 echo "a = $a" # a = 123
 5 # 在圓括號(hào)中a變量,更像是一個(gè)局部變量.
 
 用在數(shù)組初始化,如:
 1 Array=(element1,element2,element3)
 
 {xxx,yyy,zzz...}
 大括號(hào)擴(kuò)展,如:
 1 cat {file1,file2,file3} > combined_file
 2 # 把file1,file2,file3連接在一起,并且重定向到combined_file中.
 3
 4
 5 cp file22.{txt,backup}
 6 # 拷貝"file22.txt" 到"file22.backup"中
 
 一個(gè)命令可能會(huì)對(duì)大括號(hào)中的以逗號(hào)分割的文件列表起作用[1]. file globbing將對(duì)
 大括號(hào)中的文件名作擴(kuò)展.
 注意: 在大括號(hào)中,不允許有空白,除非這個(gè)空白是有意義的.
 echo {file1,file2}\ :{\ A," B",' C'}
 file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C
 {} 代碼塊.又被稱為內(nèi)部組.事實(shí)上,這個(gè)結(jié)構(gòu)創(chuàng)建了一個(gè)匿名的函數(shù).但是與函數(shù)不同的
 是,在其中聲明的變量,對(duì)于腳本其他部分的代碼來說還是可見的.如:
 bash$
 {
 local a;
 a= 123;
 }
 bash中的local申請(qǐng)的變量只能夠用在函數(shù)中.
 
 1 a=123
 2 { a=321; }
 3 echo "a = $a" # a = 321 (說明在代碼塊中對(duì)變量a所作的修改,影響了外邊的變量a)
 4
 5 # Thanks, S.C.
 
 下邊的代碼展示了在{}結(jié)構(gòu)中代碼的I/O重定向.
 
 Example 3-1. 代碼塊和I/O重定向
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # 從 /etc/fstab中讀行
 3
 4 File=/etc/fstab
 5
 6 {
 7 read line1
 8 read line2
 9 } < $File
 10
 11 echo "First line in $File is:"
 12 echo "$line1"
 13 echo
 14 echo "Second line in $File is:"
 15 echo "$line2"
 16
 17 exit 0
 18
 19 # 現(xiàn)在,你怎么分析每行的分割域
 20 # 暗示: 使用 awk.
 ################################End Script#########################################
 
 Example 3-2. 將一個(gè)代碼塊的結(jié)果保存到文件
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # rpm-check.sh
 3
 4 # 這個(gè)腳本的目的是為了描述,列表,和確定是否可以安裝一個(gè)rpm包.
 5 # 在一個(gè)文件中保存輸出.
 6 #
 7 # 這個(gè)腳本使用一個(gè)代碼塊來展示
 8
 9 SUCCESS=0
 10 E_NOARGS=65
 11
 12 if [ -z "$1" ]
 13 then
 14 echo "Usage: `basename $0` rpm-file"
 15 exit $E_NOARGS
 16 fi
 17
 18 {
 19 echo
 20 echo "Archive Description:"
 21 rpm -qpi $1 # 查詢說明
 22 echo
 23 echo "Archive Listing:"
 24 rpm -qpl $1 # 查詢列表
 25 echo
 26 rpm -i --test $1 # 查詢r(jià)pm包是否可以被安裝
 27 if [ "$?" -eq $SUCCESS ]
 28 then
 29 echo "$1 can be installed."
 30 else
 31 echo "$1 cannot be installed."
 32 fi
 33 echo
 34 } > "$1.test" # 把代碼塊中的所有輸出都重定向到文件中
 35
 36 echo "Results of rpm test in file $1.test"
 37
 38 # 查看rpm的man頁來查看rpm的選項(xiàng)
 39
 40 exit 0
 ################################End Script#########################################
 注意: 與()中的命令不同的是,{}中的代碼塊將不能正常地開啟一個(gè)新shell.[2]
 
 {} \; 路徑名.一般都在find命令中使用.這不是一個(gè)shell內(nèi)建命令.
 注意: ";"用來結(jié)束find命令序列的-exec選項(xiàng).
 
 [] test.
 test的表達(dá)式將在[]中.
 值得注意的是[是shell內(nèi)建test命令的一部分,并不是/usr/bin/test中的擴(kuò)展命令
 的一個(gè)連接.
 
 [[]] test.
 test表達(dá)式放在[[]]中.(shell關(guān)鍵字)
 具體查看[[]]結(jié)構(gòu)的討論.
 
 [] 數(shù)組元素
 Array[1]=slot_1
 echo ${Array[1]}
 
 [] 字符范圍
 在正則表達(dá)式中使用,作為字符匹配的一個(gè)范圍
 
 (()) 數(shù)學(xué)計(jì)算的擴(kuò)展
 在(())結(jié)構(gòu)中可以使用一些數(shù)字計(jì)算.
 具體參閱((...))結(jié)構(gòu).
 
 >&>>&>><
 重定向.
 scriptname >filename 重定向腳本的輸出到文件中.覆蓋文件原有內(nèi)容.
 command &>filename 重定向stdout和stderr到文件中
 command >&2 重定向command的stdout到stderr
 scriptname >>filename 重定向腳本的輸出到文件中.添加到文件尾端,如果沒有文件,
 則創(chuàng)建這個(gè)文件.
 
 進(jìn)程替換,具體見"進(jìn)程替換部分",跟命令替換極其類似.
 (command)>
 <(command)
 
 <和> 可用來做字符串比較
 <和> 可用在數(shù)學(xué)計(jì)算比較
 
 << 重定向,用在"here document"
 
 <<< 重定向,用在"here string"
 
 <,> ASCII比較
 1 veg1=carrots
 2 veg2=tomatoes
 3
 4 if [[ "$veg1" < "$veg2" ]]
 5 then
 6 echo "Although $veg1 precede $veg2 in the dictionary,"
 7 echo "this implies nothing about my culinary preferences."
 8 else
 9 echo "What kind of dictionary are you using, anyhow?"
 10 fi
 
 \<,\> 正則表達(dá)式中的單詞邊界.如:
 bash$grep '\' textfile
 
 | 管道.分析前邊命令的輸出,并將輸出作為后邊命令的輸入.這是一種產(chǎn)生命令鏈的
 好方法.
 1 echo ls -l | sh
 2 # 傳遞"echo ls -l"的輸出到shell中,
 3 #+ 與一個(gè)簡單的"ls -l"結(jié)果相同.
 4
 5
 6 cat *.lst | sort | uniq
 7 # 合并和排序所有的".lst"文件,然后刪除所有重復(fù)的行.
 
 管道是進(jìn)程間通訊的一個(gè)典型辦法,將一個(gè)進(jìn)程的stdout放到另一個(gè)進(jìn)程的stdin中.
 標(biāo)準(zhǔn)的方法是將一個(gè)一般命令的輸出,比如cat或echo,傳遞到一個(gè)過濾命令中(在這個(gè)
 過濾命令中將處理輸入),得到結(jié)果,如:
 cat $filename1 | $filename2 | grep $search_word
 
 當(dāng)然輸出的命令也可以傳遞到腳本中.如:
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # uppercase.sh : 修改輸出,全部轉(zhuǎn)換為大寫
 3
 4 tr 'a-z' 'A-Z'
 5 # 字符范圍必須被""引用起來
 6 #+ 來阻止產(chǎn)生單字符的文件名.
 7
 8 exit 0
 ################################End Script#########################################
 
 現(xiàn)在讓我們輸送ls -l的輸出到一個(gè)腳本中.
 bash$ ls -l | ./uppercase.sh
 -RW-RW-R-- 1 BOZO BOZO 109 APR 7 19:49 1.TXT
 -RW-RW-R-- 1 BOZO BOZO 109 APR 14 16:48 2.TXT
 -RW-R--R-- 1 BOZO BOZO 725 APR 20 20:56 DATA-FILE
 
 注意:管道中的一個(gè)進(jìn)程的stdout必須被下一個(gè)進(jìn)程作為stdin讀入.否則,數(shù)據(jù)流會(huì)阻
 塞,并且管道將產(chǎn)生非預(yù)期的行為.
 如:
 1 cat file1 file2 | ls -l | sort
 2 #從"cat file1 file2"中的輸出并沒出現(xiàn)
 
 作為子進(jìn)程的運(yùn)行的管道,不能夠改變腳本的變量.
 1 variable="initial_value"
 2 echo "new_value" | read variable
 3 echo "variable = $variable" #variable = initial_value
 如果管道中的某個(gè)命令產(chǎn)生了一個(gè)異常,并中途失敗,那么這個(gè)管道將過早的終止.
 這種行為被叫做a broken pipe,并且這種狀態(tài)下將發(fā)送一個(gè)SIGPIPE信號(hào).
 
 >| 強(qiáng)制重定向(即使設(shè)置了noclobber選項(xiàng)--就是-C選項(xiàng)).這將強(qiáng)制的覆蓋一個(gè)現(xiàn)存文件.
 
 || 或-邏輯操作.
 
 & 后臺(tái)運(yùn)行命令.一個(gè)命令后邊跟一個(gè)&,將表示在后臺(tái)運(yùn)行.
 bash$sleep 10 &
 [1] 850
 [1]+ Done sleep 10
 在一個(gè)腳本中,命令和循環(huán)都可能運(yùn)行在后臺(tái).
 
 Example 3-3. 在后臺(tái)運(yùn)行一個(gè)循環(huán)
 ################################Start Script#######################################
 1 #!/bin/bash
 2 #background-loop.sh
 3
 4 for i in 1 2 3 4 5 6 7 8 9 10 #第一個(gè)循環(huán)
 5 do
 6 echo -n "$i"
 7 done& #在后臺(tái)運(yùn)行這個(gè)循環(huán)
 8 #在第2個(gè)循環(huán)之后,將在某些時(shí)候執(zhí)行.
 9
 10 echo #這個(gè)'echo'某些時(shí)候?qū)⒉粫?huì)顯示.
 11
 12 for i in 11 12 13 14 15 16 17 18 19 20 #第二個(gè)循環(huán)
 13 do
 14 echo -n "$i"
 15 done
 16
 17 echo #這個(gè)'echo'某些時(shí)候?qū)⒉粫?huì)顯示.
 18
 19 #--------------------------------------------------------
 20
 21 #期望的輸出應(yīng)該是
 22 #1 2 3 4 5 6 7 8 9 10
 23 #11 12 13 14 15 16 17 18 19 20
 24
 25 #然而實(shí)際的結(jié)果有可能是
 26 #11 12 13 14 15 16 17 18 19 20
 27 #1 2 3 4 5 6 7 8 9 10 bozo $
 28 #(第2個(gè)'echo'沒執(zhí)行,為什么?)
 29
 30 #也可能是
 31 #1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 32 #(第1個(gè)'echo'沒執(zhí)行,為什么?)
 33
 34 #非常少見的執(zhí)行結(jié)果,也有可能是:
 35 #11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20
 36 #前臺(tái)的循環(huán)先于后臺(tái)的執(zhí)行
 37
 38 exit 0
 39
 40 # Nasimuddin Ansari 建議加一句 sleep 1
 41 #+ 在 6行和14行的 echo -n "$i"之后加
 42 #+ 將看到一些樂趣
 ################################End Script#########################################
 注意:在一個(gè)腳本內(nèi)后臺(tái)運(yùn)行一個(gè)命令,有可能造成這個(gè)腳本的掛起,等待一個(gè)按鍵
 響應(yīng).幸運(yùn)的是,我們可以在Example 11-24附近,看到這個(gè)問題的解決辦法.
 
 && 與-邏輯操作.
 
 - 選項(xiàng),前綴.在所有的命令內(nèi)如果想使用選項(xiàng)參數(shù)的話,前邊都要加上"-".
 
 COMMAND -[Option1][Option2][...]
 ls -al
 sort -dfu $filename
 set -- $variable
 
 1 if [ $file1 -ot $file2 ]
 2 then
 3 echo "File $file1 is older than $file2."
 4 fi
 5
 6 if [ "$a" -eq "$b" ]
 7 then
 8 echo "$a is equal to $b."
 9 fi
 10
 11 if [ "$c" -eq 24 -a "$d" -eq 47 ]
 12 then
 13 echo "$c equals 24 and $d equals 47."
 14 fi
 
 - 用于重定向 stdin 或 stdout.
 
 ################################Start Script#######################################
 1 (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
 2 # 從一個(gè)目錄移動(dòng)整個(gè)目錄樹到另一個(gè)目錄
 3 # [courtesy Alan Cox <a.cox@swansea.ac.uk>, with a minor change]
 4
 5 # 1) cd /source/directory 源目錄
 6 # 2) && 與操作,如果cd命令成功了,那么就執(zhí)行下邊的命令
 7 # 3) tar cf - . 'c'創(chuàng)建一個(gè)新文檔,'f'后邊跟'-'指定目標(biāo)文件作為stdout
 8 # '-'后邊的'f'(file)選項(xiàng),指明作為stdout的目標(biāo)文件.
 9 # 并且在當(dāng)前目錄('.')執(zhí)行.
 10 # 4) | 管道...
 11 # 5) ( ... ) 一個(gè)子shell
 12 # 6) cd /dest/directory 改變當(dāng)前目錄到目標(biāo)目錄.
 13 # 7) && 與操作,同上.
 14 # 8) tar xpvf - 'x'解檔,'p'保證所有權(quán)和文件屬性,
 15 # 'v'發(fā)完整消息到stdout
 16 # 'f'后邊跟'-',從stdin讀取數(shù)據(jù)
 17 #
 18 # 注意:'x' 是一個(gè)命令, 'p', 'v', 'f' 是選項(xiàng).
 19 # Whew!
 20
 21
 22
 23 # 更優(yōu)雅的寫法應(yīng)該是
 24 # cd source/directory
 25 # tar cf - . | (cd ../dest/directory; tar xpvf -)
 26 #
 27 # 當(dāng)然也可以這么寫:
 28 # cp -a /source/directory/* /dest/directory
 29 # 或者:
 30 # cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
 31 # 如果在/source/directory中有隱藏文件的話.
 ################################End Script#########################################
 
 ################################Start Script#######################################
 1 bunzip2 linux-2.6.13.tar.bz2 | tar xvf -
 2 # --未解壓的tar文件-- | --然后把它傳遞到"tar"中--
 3 # 如果 "tar" 沒能夠正常的處理"bunzip2",
 4 # 這就需要使用管道來執(zhí)行2個(gè)單獨(dú)的步驟來完成它.
 5 # 這個(gè)練習(xí)的目的是解檔"bzipped"的kernel源文件.
 ################################End Script#########################################
 注意:在上邊這個(gè)例子中'-'不太象是bash的操作符,而更像是tar的參數(shù).
 bash$echo "whatever" | cat -
 whatever
 
 在需要一個(gè)文件名的地方,-重定向輸出到stdout(如在tar和cf命令中),或者從
 stdin中接受輸入,而不是從一個(gè)文件中接受輸入.這是在管道中作為一個(gè)過濾
 器,來使用文件定位工具的一種辦法.
 bash$file
 用法: file [-bciknvzl] [-f namefile] [-m magicfiles] file...
 上邊這個(gè)例子file將會(huì)出錯(cuò),提示你如何使用file命令.
 
 添加一個(gè)"-"將得到一個(gè)更有用的結(jié)果.這將使得shell等待用戶輸入.
 bash$file -
 abc
 standard input: ASCII text
 
 bash$file -
 #!/bin/bash
 standard input: Bourn-Again shell script tesxt executable
 
 現(xiàn)在命令從stdin中接受了輸入,并分析它.
 
 "-"常用于管道后邊的命令,具體參看33.7節(jié),來看使用技巧.
 使用diff命令來和另一個(gè)文件的一部分進(jìn)行比較.
 grep Linux file1 | diff file2 -
 
 最后,一個(gè)真實(shí)世界的使用tar命令的例子.
 
 Example 3-4. 備份最后一天所有修改的文件.
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 # 在一個(gè)"tarball"中(經(jīng)過tar和gzip處理過的文件)
 4 #+ 備份最后24小時(shí)當(dāng)前目錄下d所有修改的文件.
 5
 6 BACKUPFILE=backup-$(date +%m-%d-%Y)
 7 # 在備份文件中嵌入時(shí)間.
 8 # Thanks, Joshua Tschida, for the idea.
 9 archive=${1:-$BACKUPFILE}
 10 # 如果在命令行中沒有指定備份文件的文件名,
 11 #+ 那么將默認(rèn)使用"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 #+ 如果在發(fā)現(xiàn)太多的文件的時(shí)候,或者是如果文件
 20 #+ 名包括空格的時(shí)候,將執(zhí)行失敗.
 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 # 對(duì)于其他風(fēng)格的UNIX便于移植,但是比較慢.
 30 # -------------------------------------------------------------------
 31
 32
 33 exit 0
 ################################End Script#########################################
 
 注意:以"-"開頭的文件名在使用"-"作為重定向操作符的時(shí)候,可能會(huì)產(chǎn)生問題.
 應(yīng)該寫一個(gè)腳本來檢查這個(gè)問題,并給這個(gè)文件加上合適的前綴.如:
 ./-FILENAME, $PWD/-FILENAME,或$PATHNAME/-FILENAME.
 
 如果變量的值以"-"開頭,可能也會(huì)引起問題.
 1 var="-n"
 2 echo $var
 3 #具有"echo -n"的效果了,這樣什么都不會(huì)輸出的.
 
 - 之前工作的目錄."cd -"將回到之前的工作目錄,具體請(qǐng)參考"$OLDPWD"環(huán)境變量.
 注意:一定要和之前討論的重定向功能分開,但是只能依賴上下文區(qū)分.
 
 - 算術(shù)減號(hào).
 
 = 算術(shù)等號(hào),有時(shí)也用來比較字符串.
 1 a=28
 2 echo $a # 28
 
 + 算術(shù)加號(hào),也用在正則表達(dá)式中.
 + 選項(xiàng),對(duì)于特定的命令來說使用"+"來打開特定的選項(xiàng),用"-"來關(guān)閉特定的選項(xiàng).
 
 % 算術(shù)取模運(yùn)算.也用在正則表達(dá)式中.
 
 ~ home目錄.相當(dāng)于$HOME變量.~bozo是bozo的home目錄,并且ls ~bozo將列出其中的
 內(nèi)容. ~/就是當(dāng)前用戶的home目錄,并且ls ~/將列出其中的內(nèi)容,如:
 bash$ echo ~bozo
 /home/bozo
 
 bash$ echo ~
 /home/bozo
 
 bash$ echo ~/
 /home/bozo/
 
 bash$ echo ~:
 /home/bozo:
 
 bash$ echo ~nonexistent-user
 ~nonexistent-user
 
 ~+ 當(dāng)前工作目錄,相當(dāng)于$PWD變量.
 
 ~- 之前的工作目錄,相當(dāng)于$OLDPWD內(nèi)部變量.
 
 =~ 用于正則表達(dá)式,這個(gè)操作將在正則表達(dá)式匹配部分講解,只有version3才支持.
 
 ^ 行首,正則表達(dá)式中表示行首."^"定位到行首.
 
 
 控制字符
 修改終端或文本顯示的行為.控制字符以CONTROL + key組合.
 控制字符在腳本中不能正常使用.
 Ctl-B 光標(biāo)后退,這應(yīng)該依賴于bash輸入的風(fēng)格,默認(rèn)是emacs風(fēng)格的.
 Ctl-C Break,終止前臺(tái)工作.
 Ctl-D 從當(dāng)前shell登出(和exit很像)
 "EOF"(文件結(jié)束符).這也能從stdin中終止輸入.
 在console或者在xterm window中輸入的時(shí)候,Ctl-D將刪除光標(biāo)下字符.
 當(dāng)沒有字符時(shí),Ctrl-D將退出當(dāng)前會(huì)話.在xterm window也有關(guān)閉窗口
 的效果.
 Ctl-G beep.在一些老的終端,將響鈴.
 Ctl-H backspace,刪除光標(biāo)前邊的字符.如:
 1 #!/bin/bash
 2 # 在一個(gè)變量中插入Ctl-H
 3
 4 a="^H^H" # 兩個(gè) Ctl-H (backspaces).
 5 echo "abcdef" # abcdef
 6 echo -n "abcdef$a " # abcd f
 7 # 注意結(jié)尾的空格 ^ ^ 兩個(gè) twice.
 8 echo -n "abcdef$a" # abcdef
 9 # 結(jié)尾沒有空格 沒有 backspace 的效果了(why?).
 10 # 結(jié)果并不像期望的那樣
 11 echo; echo
 Ctl-I 就是tab鍵.
 Ctl-J 新行.
 Ctl-K 垂直tab.(垂直tab?新穎,沒聽過)
 作用就是刪除光標(biāo)到行尾的字符.
 Ctl-L clear,清屏.
 Ctl-M 回車
 ################################Start Script#######################################
 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. Press Enter. \x0d'
 5 #當(dāng)然,'0d'就是二進(jìn)制的回車.
 6 echo >&2 # '-s'參數(shù)使得任何輸入都不將回顯出來
 7 #+ 所以,明確的重起一行是必要的.
 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 # 關(guān)于垂直制表符效果的一個(gè)更好的例子見下邊:
 18
 19 var=$'\x0aThis is the bottom line\x0bThis is the top line\x0a'
 20 echo "$var"
 21 # 這句與上邊的例子使用的是同樣的辦法,然而:
 22 echo "$var" | col
 23 # 這將造成垂直制表符右邊的部分在左邊部分的上邊.
 24 # 這也解釋了為什么我們要在行首和行尾加上一個(gè)換行符--
 25 #+ 來避免一個(gè)混亂的屏幕輸出.
 26
 27 # Lee Maschmeyer的解釋:
 28 # ---------------------
 29 # In the [first vertical tab example] . . . the vertical tab
 29 # 在這里[第一個(gè)垂直制表符的例子中] . . . 這個(gè)垂直制表符
 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 # 它可以用來在一個(gè)打印機(jī)上打印上標(biāo).
 35 # col的作用,可以用來模仿VT的合適的行為.
 36
 37 exit 0
 ################################End Script#########################################
 Ctl-Q 繼續(xù)(等價(jià)于XON字符),這個(gè)繼續(xù)的標(biāo)準(zhǔn)輸入在一個(gè)終端里
 Ctl-S 掛起(等價(jià)于XOFF字符),這個(gè)被掛起的stdin在一個(gè)終端里,用Ctl-Q恢復(fù)
 Ctl-U 刪除光標(biāo)到行首的所有字符,在某些設(shè)置下,刪除全行.
 Ctl-V 當(dāng)輸入字符時(shí),Ctl-V允許插入控制字符.比如,下邊2個(gè)例子是等價(jià)的
 echo -e '\x0a'
 echo
 Ctl-V在文本編輯器中十分有用,在vim中一樣.
 Ctl-W 刪除當(dāng)前光標(biāo)到前邊的最近一個(gè)空格之間的字符.
 在某些設(shè)置下,刪除到第一個(gè)非字母或數(shù)字的字符.
 Ctl-Z 終止前臺(tái)工作.
 
 空白部分
 分割命令或者是變量.包括空格,tab,空行,或任何它們的組合.
 在一些特殊情況下,空白是不允許的,如變量賦值時(shí),會(huì)引起語法錯(cuò)誤.
 空白行在腳本中沒有效果.
 "$IFS",對(duì)于某些命令輸入的特殊變量分割域,默認(rèn)使用的是空白.
 如果想保留空白,使用引用.
 
 注意事項(xiàng):
 [1] shell做大括號(hào)的命令擴(kuò)展.但是命令本身需要對(duì)擴(kuò)展的結(jié)果作處理.
 [2] 例外:在pipe中的一個(gè)大括號(hào)中的代碼段可能運(yùn)行在一個(gè)子shell中.
 1 ls | { read firstline; read secondline; }
 2 # 錯(cuò)誤,在打括號(hào)中的代碼段,將運(yùn)行到子shell中.
 3 #+ 所以ls的輸出將不能傳遞到代碼塊中.
 4 echo "First line is $firstline; second line is $secondline" # 不能工作
 5
 6 # Thanks, S.C.
 [3] 換行符也被認(rèn)為是空白.這也解釋了為什么一個(gè)空行也會(huì)被認(rèn)為是空白.
 
 
 
 第4章 變量和參數(shù)的介紹
 ======================
 
 4.1 變量替換
 ------------
 $ 變量替換操作符
 只有在變量被聲明,賦值,unset或exported或者是在變量代表一個(gè)signal的時(shí)候,
 變量才會(huì)是以本來的面目出現(xiàn)在腳本里.變量在被賦值的時(shí)候,可能需要使用"=",
 read狀態(tài)或者是在循環(huán)的頭部.
 在""中還是會(huì)發(fā)生變量替換,這被叫做部分引用,或叫弱引用.而在''中就不會(huì)發(fā)生變
 量替換,這叫做全引用,也叫強(qiáng)引用.具體見第5章的討論.
 
 注意:$var與${var}的區(qū)別,不加{},在某些上下文將引起錯(cuò)誤,為了安全,使用2.
 具體見9.3節(jié) 參數(shù)替換.
 
 Example 4-1. 變量賦值和替換
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 # 變量賦值和替換
 4
 5 a=375
 6 hello=$a
 7
 8 #-------------------------------------------------------------------------
 9 # 強(qiáng)烈注意,在賦值的前后一定不要有空格.
 10 # 如果有空格會(huì)發(fā)生什么?
 11
 12 # 如果"VARIABLE =value",
 13 # ^
 14 #+ 腳本將嘗試運(yùn)行一個(gè)"VARIABLE"的命令,帶著一個(gè)"=value"參數(shù).
 15
 16 # 如果"VARIABLE= value",
 17 # ^
 18 #+ script tries to run "value" command with
 18 #+ 腳本將嘗試運(yùn)行一個(gè)"value"的命令,帶著
 19 #+ the environmental variable "VARIABLE" set to "".
 19 #+ 一個(gè)被賦成""值的環(huán)境變量"VARIABLE".
 20 #-------------------------------------------------------------------------
 21
 22
 23 echo hello # 沒有變量引用,不過是個(gè)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" 將給出不同的結(jié)果.
 37 # ^ ^
 38 # Quoting a variable preserves whitespace.
 38 # 引用一個(gè)變量將保留其中的空白,當(dāng)然,如果是變量替換就不會(huì)保留了.
 39
 40 echo
 41
 42 echo '$hello' # $hello
 43 # ^ ^
 44 # 全引用的作用
 45 #+ 將導(dǎo)致"$"變成一個(gè)單獨(dú)的字符.
 46
 47 # 注意兩種引用不同的效果
 48
 49
 50 hello= # 設(shè)置為空值
 51 echo "\$hello (null value) = $hello"
 52 # 注意設(shè)置一個(gè)變量為空,與unset它,不是一回事,雖然看起來一樣
 53 #
 54
 55 # --------------------------------------------------------------
 56
 57 # 可以在同一行上設(shè)置多個(gè)變量.
 58 #+ 要以空白分隔
 59 # 小心,這會(huì)降低可讀性,和可移植性.
 60
 61 var1=21 var2=22 var3=$V3
 62 echo
 63 echo "var1=$var1 var2=$var2 var3=$var3"
 64
 65 # 在老版本的"sh"上,可能會(huì)有問題.
 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 #+ 其實(shí)和前邊設(shè)置為空值得作用是一樣的.
 85 echo "uninitialized_variable = $uninitialized_variable"
 86 # 還是一個(gè)空值
 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#########################################
 注意: 一個(gè)空值變量,或者是根本就沒聲明的變量,在賦值之前使用它可能會(huì)引起問題.
 但是還是可以用來做算術(shù)運(yùn)算
 ################################Start Script#######################################
 1 echo "$uninitialized" # (blank line)
 2 let "uninitialized += 5" # Add 5 to it.
 3 echo "$uninitialized" # 5
 4
 5 # 結(jié)論:
 6 # 對(duì)于一個(gè)空值變量在做算術(shù)操作的時(shí)候,就好像它的值為0一樣.
 8 # This is undocumented (and probably non-portable) behavior.
 7 # 這并沒被文檔化(可能是不可移植)的行為.
 ################################End Script#########################################
 具體參考 Example 11-21
 
 
 4.2 變量賦值
 ------------
 = 賦值操作符(前后都不能有空白)
 不要與-eq混淆,那個(gè)是test,并不是賦值.
 注意,=也可被用來做test操作,這依賴于上下文.
 
 Example 4-2. 一般的變量賦值
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # "裸體"變量
 3
 4 echo
 5
 6 # 變量什么時(shí)候是"裸體"的,比如前邊少了$的時(shí)候.
 7 # 當(dāng)它被賦值的時(shí)候,而不是被引用的時(shí)候.
 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循環(huán)中
 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命令狀態(tài)中
 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 # 現(xiàn)在讓我們來點(diǎn)小變化
 9
 10 a=`echo Hello!` # 把echo命令的結(jié)果傳給變量a
 11 echo $a
 12 # 注意,如果在命令擴(kuò)展結(jié)構(gòu)中使用一個(gè)(!)的話,在命令行中將不能工作
 13 #+ 因?yàn)檫@觸發(fā)了Bash的"歷史機(jī)制".
 14 # 但是,在校本里邊使用的話,歷史功能是被關(guān)閉的,所以就能夠正常運(yùn)行.
 15
 16
 17 a=`ls -l` # 把ls -l的結(jié)果給a
 18 echo $a # 別忘了,這么引用的話,ls的結(jié)果中的所有空白部分都沒了(包括換行)
 19 echo
 20 echo "$a" # 這么引用就正常了,保留了空白
 21 # (具體參閱章節(jié)"引用")
 22
 23 exit 0
 ################################End Script#########################################
 使用$(...)機(jī)制進(jìn)行的變量賦值(除去使用``來賦值的另外一種新方法).事實(shí)上這兩種方法都是
 命令替換的一種形式.
 # 來自于/ect/rc.d/rc.local
 R=$(cat /ect/redhat-release)
 arch=$(uname -m)
 
 
 4.3 Bash變量是不分類型的
 ------------------------
 不像其他程序語言一樣,Bash并不對(duì)變量區(qū)分"類型".本質(zhì)上,Bash變量都是字符串.
 但是依賴于上下文,Bash也允許比較操作和算術(shù)操作.決定這些的關(guān)鍵因素就是,變量中的值
 是否只有數(shù)字.
 
 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 # 還是整型
 8
 9
 10 b=${a/23/BB} # 將23替換成BB
 11 # 這將把b變量從整型變?yōu)閟tring
 12 echo "b = $b" # b = BB35
 13 declare -i b # 即使使用declare命令也不會(huì)對(duì)此有任何幫助,9.4節(jié)有解釋
 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變?yōu)橐粋€(gè)整形
 24 echo "d = $d" # d = 2334
 25 let "d += 1" # 2334 + 1 =
 26 echo "d = $d" # d = 2335
 27 echo
 28
 29 # 關(guān)于空變量怎么樣?
 30 e=""
 31 echo "e = $e" # e =
 32 let "e += 1" # 算術(shù)操作允許一個(gè)空變量?
 33 echo "e = $e" # e = 1
 34 echo # 空變量將轉(zhuǎn)換成一個(gè)整型變量
 35
 36 # 關(guān)于未聲明的變量怎么樣?
 37 echo "f = $f" # f =
 38 let "f += 1" # 算術(shù)操作允許么?
 39 echo "f = $f" # f = 1
 40 echo # 未聲明的變量將轉(zhuǎn)換成一個(gè)整型變量
 41
 42
 43
 44 # 所以說Bash中的變量都是無類型的.
 45
 46 exit 0
 ################################End Script#########################################
 
 
 4.4 特殊的變量類型
 ------------------
 local variables
 這種變量只有在代碼塊或者是函數(shù)中才可見(具體見23.2和23章)
 environmental variables
 這種變量將改變用戶接口和shell的行為.
 
 在一般的上下文中,每個(gè)進(jìn)程都有自己的環(huán)境,就是一組保持進(jìn)程可能引用的信息的
 變量.這種情況下,shell于一個(gè)一般進(jìn)程是相同的.
 
 每次當(dāng)shell啟動(dòng)時(shí),它都將創(chuàng)建自己的環(huán)境變量.更新或者添加新的環(huán)境變量,將導(dǎo)
 致shell更新它的環(huán)境,同時(shí)也會(huì)影響所有繼承自這個(gè)環(huán)境的所有子進(jìn)程(由這個(gè)命令
 導(dǎo)致的).
 
 注意:分配給環(huán)境變量的空間是受限的.創(chuàng)建太多的環(huán)境變量將引起空間溢出,這會(huì)引
 起問題.
 關(guān)于eval命令,具體見第11章
 bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"
 bash$ du
 bash: /usr/bin/du: Argument list too long
 
 如果一個(gè)腳本設(shè)置了環(huán)境變量,需要export它,來通知本腳本的環(huán)境,這是export
 命令的功能,關(guān)于export命令,具體見11章.
 
 腳本只能對(duì)它產(chǎn)生的子進(jìn)程export變量.一個(gè)從命令行被調(diào)用的腳本export的變量,將
 不能影響調(diào)用這個(gè)腳本的那個(gè)命令行shell的環(huán)境.
 
 positional parameters
 就是從命令行中傳進(jìn)來的參數(shù),$0, $1, $2, $3...
 
 $0就是腳本文件的名字,$1是第一個(gè)參數(shù),$2為第2個(gè)...,參見[1](有$0的說明),$9
 以后就需要打括號(hào)了,如${10},${11},${12}...
 兩個(gè)值得注意的變量$*和$@(第9章有具體的描述),表示所有的位置參數(shù).
 
 Example 4-5 位置參數(shù)
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 # 作為用例,調(diào)用這個(gè)腳本至少需要10個(gè)參數(shù),如
 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 # 添加./是為了當(dāng)前目錄
 11 echo "The name of this script is \"`basename $0`\"."
 12 # 去掉目錄信息,具體見'basename'命令
 13
 14 echo
 15
 16 if [ -n "$1" ] # 測(cè)試變量被被引用
 17 then
 18 echo "Parameter #1 is $1" # "#"沒被轉(zhuǎn)義
 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的參數(shù)必須出現(xiàn)在{}中.
 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" ] #$#是傳到腳本里的位置參數(shù)的個(gè)數(shù)
 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#########################################
 {}標(biāo)記法是一種很好的使用位置參數(shù)的方法.這也需要間接引用(見Example 34-2)
 1 args=$# # 位置參數(shù)的個(gè)數(shù)
 2 lastarg=${!args}
 3 # 或: lastarg=${!#}
 4 # 注意 lastarg=${!$#} 將報(bào)錯(cuò)
 
 一些腳本可能會(huì)依賴于使用不同的調(diào)用名字,而表現(xiàn)出不同的行為,這樣一般都需要
 判斷$0,而其他的名字都是通過ln命令產(chǎn)生的鏈接.(具體參見Example 12-2)
 
 如果腳本需要一個(gè)命令行參數(shù),而調(diào)用的時(shí)候,沒用這個(gè)參數(shù),這就有可能造成分配一個(gè)
 空變量,這樣估計(jì)就會(huì)引起問題.一種解決辦法就是在這個(gè)位置參數(shù),和相關(guān)的變量后
 邊,都添加一個(gè)額外的字符.具體見下邊的例子.
 ################################Start Script#######################################
 1 variable1_=$1_ # 而不是 variable1=$1
 2 # 這將阻止一個(gè)錯(cuò)誤,即使在調(diào)用時(shí)沒使用這個(gè)位置參數(shù).
 3
 4 critical_argument01=$variable1_
 5
 6 # 這個(gè)擴(kuò)展的字符是可以被消除掉的,就像這樣.
 7 variable1=${variable1_/_/}
 8 # 副作用就是$variable1_多了一個(gè)下劃線
 9 # 這里使用了一個(gè)參數(shù)替換模版(后邊會(huì)有具體的討論)
 10 # (Leaving out the replacement pattern results in a deletion.)
 10 # (在一個(gè)刪除動(dòng)作中,節(jié)省了一個(gè)替換模式)
 11
 12
 13 # 一個(gè)解決這種問題的更簡單的做法就是,判斷一下這個(gè)位置參數(shù)是否傳遞下來了
 14 if [ -z $1 ]
 15 then
 16 exit $E_MISSING_POS_PARAM
 17 fi
 18
 19
 20 # 但是上邊的方法將可能產(chǎn)生一個(gè)意外的副作用
 21 # 參數(shù)替換的更好的辦法應(yīng)該是:
 22 # ${1:-$DefaultVal}
 23 # 具體察看"Parameter Substition"節(jié)
 24 #+ 在第9章
 ################################End Script#########################################
 
 
 Example 4-6 wh,whois節(jié)點(diǎn)名字查詢
 ################################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 # 把這個(gè)腳本重命名為'wh',然后放到/usr/local/bin下
 8
 9 # 需要3個(gè)符號(hào)鏈接
 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 # 檢查腳本名字,然后調(diào)用合適的服務(wù)器
 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
 31
 32 exit $?
 ################################End Script#########################################
 
 shift shift命令重新分配位置參數(shù),其實(shí)就是向左移動(dòng)一個(gè)位置.
 $1 <--- $2, $2 <--- $3, $3 <--- $4, 等等.
 老的$1將消失,但是$0(腳本名)是不會(huì)改變的.如果你使用了大量的位置參數(shù),那么
 shift命令允許你存取超過10個(gè)參數(shù).雖然{}表示法也允許這樣.
 
 
 Example 4-7 使用shift
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # 使用'shift'來穿過所有的位置參數(shù).
 3
 4 # 把這個(gè)腳本命名為shft,
 5 #+ 并且使用一些參數(shù)來調(diào)用它,如:
 6 # ./shft a b c def 23 skidoo
 7
 8 until [ -z "$1" ] # 知道所有參數(shù)都用光
 9 do
 10 echo -n "$1 "
 11 shift
 12 done
 13
 14 echo # 額外的換行.
 15
 16 exit 0
 ################################End Script#########################################
 在將參數(shù)傳遞到函數(shù)中時(shí),shift的工作方式也基本差不多.具體見Example 33-15
 
 注意事項(xiàng):
 [1] 進(jìn)程調(diào)用設(shè)置$0參數(shù)的腳本.一般的,這個(gè)參數(shù)就是腳本名字.具體察看execv的man頁.
 
 
 
 第5章 引用(翻譯的可能有問題,特指引號(hào))
 ======================================
 引號(hào)的特殊效果就是,保護(hù)字符串中的特殊字符不被shell或者是shell腳本重新解釋或者擴(kuò)展.
 (我們這里所說的"特殊"指的是一些字符在shell中具有的特殊意義,比如*)
 如:
 bash$ ls -l [Vv]*
 -rw-rw-r-- 1 bozo bozo 324 Apr 2 15:05 VIEWDATA.BAT
 -rw-rw-r-- 1 bozo bozo 507 May 4 14:25 vartrace.sh
 -rw-rw-r-- 1 bozo bozo 539 Apr 14 17:11 viewdata.sh
 
 bash$ ls -l '[Vv]*'
 ls: [Vv]*: No such file or directory
 
 在我們一般的生活中,引號(hào)內(nèi)的內(nèi)容往往有特殊的含義,而在Bash中,當(dāng)我們引用一個(gè)字符串,
 我們是保護(hù)它的字面含義.
 
 特定的程序和工具能夠重新解釋或擴(kuò)展特殊的字符.引用的一個(gè)重要的作用就是保護(hù)命令行中
 的參數(shù),但還是允許正在調(diào)用的程序來擴(kuò)展它.
 bash$ grep '[Ff]irst' *.txt
 file1.txt:This is the first line of file1.txt.
 file2.txt:This is the First line of file2.txt.
 
 注意 grep [Ff]irst *.txt在Bash下的行為(其實(shí)就是正則表達(dá)式么),[1]
 
 引用還可以抑制echo命令的換行作用.
 
 bash$ echo $(ls -l)
 total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
 
 bash$ echo "$(ls -l)"
 total 8
 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh
 -rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh
 
 
 5.1 引用變量
 ------------
 在一個(gè)雙引號(hào)中直接使用變量名,一般都是沒有問題的.它阻止了所有在引號(hào)中的特殊字符的
 重新解釋--包括變量名[2]--但是$,`和\除外.[3]保留$,作為特殊字符的意義,是為了能夠在雙
 引號(hào)中也能夠正常地引用變量("$var").這樣在""中可以使用變量所表達(dá)的值(Example 4-1).
 
 使用""來防止單詞分割.[4]如果在參數(shù)列表中使用雙引號(hào),將使得雙引號(hào)中的參數(shù)作為一個(gè)參
 數(shù).即使雙引號(hào)中的字符串包含多個(gè)單詞(也就是包含空白部分),也不會(huì)變?yōu)槎鄠€(gè)參數(shù),如:
 1 variable1="a variable containing five words"
 2 COMMAND This is $variable1 # COMMAND將以7個(gè)參數(shù)來執(zhí)行
 3 # "This" "is" "a" "variable" "containing" "five" "words"
 4
 5 COMMAND "This is $variable1" # COMMAND將以1個(gè)參數(shù)來執(zhí)行
 6 # "This is a variable containing five words"
 7
 8
 9 variable2="" # 空值
 10
 11 COMMAND $variable2 $variable2 $variable2 # COMMAND將不帶參數(shù)執(zhí)行
 12 COMMAND "$variable2" "$variable2" "$variable2" # COMMAND將以3個(gè)空參數(shù)來執(zhí)行
 13 COMMAND "$variable2 $variable2 $variable2" # COMMAND將以1個(gè)參數(shù)來執(zhí)行(2空格)
 用雙引號(hào)把參數(shù)封到echo中是很有必要的,只有在單詞分隔或時(shí)保留空白時(shí)的時(shí)候可能
 有些問題.
 
 Example 5-1 echo一些詭異的變量
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # weirdvars.sh: echo詭異的變量
 3
 4 var="'(]\\{}\$\""
 5 echo $var # '(]\{}$"
 6 echo "$var" # '(]\{}$" 并沒有什么不同
 7
 8 echo
 9
 10 IFS='\'
 11 echo $var # '(] {}$" \ 轉(zhuǎn)換成空格了?明顯和IFS有關(guān)系么!又不傻!
 12 echo "$var" # '(]\{}$"
 13
 14 exit 0
 ################################End Script#########################################
 
 單引號(hào)操作總體上和""很像,但不允許引用變量.因?yàn)?的特殊含義被關(guān)閉了.在''中除了',其他
 字符都沒有特殊的含義了.所以單引號(hào)比雙引號(hào)嚴(yán)格.
 因?yàn)榧词故荺,在''中都被關(guān)閉了,所以你想在''中顯示'的含義,將得不到預(yù)期的效果.
 1 echo "Why can't I write 's between single quotes"
 2
 3 echo
 4
 5 # 一種繞彎的方法
 6 echo 'Why can'\''t I write '"'"'s between single quotes'
 7 # |-------| |----------| |-----------------------|
 8 # 包含了2個(gè)單引號(hào)字符,原書好像有錯(cuò)誤
 
 注意事項(xiàng):
 [1] 除非當(dāng)前目錄下,正好有個(gè)叫first的文件.
 [2] 即使是變量的值也是有副作用的(見下邊)
 [3] 如果在""中包含"!"的話,在命令行中將會(huì)出現(xiàn)錯(cuò)誤.因?yàn)檫@個(gè)"!"被當(dāng)作歷史命令來解釋了.
 在一個(gè)腳本中,這種情況是不會(huì)發(fā)生的,因?yàn)樵谀_本中,Bash歷史記錄被關(guān)閉了.
 
 下邊是一些關(guān)于"\"一些不協(xié)調(diào)的行為.
 bash$ echo hello\!
 hello!
 
 bash$ echo "hello\!"
 hello\!
 
 bash$ echo -e x\ty
 xty
 
 bash$ echo -e "x\ty"
 x y
 
 [4] "單詞分隔",在這個(gè)上下文中意味著,將一個(gè)字符串分隔為一些分離的參數(shù).
 
 
 5.2 轉(zhuǎn)義(\)
 -----------
 轉(zhuǎn)義是一種引用單個(gè)字符的方法.一個(gè)具有特殊含義的字符前邊放上一個(gè)轉(zhuǎn)義符(\)就告訴shell
 這個(gè)字符失去了特殊的含義.
 值得注意的是,在某些特定的命令和工具中,比如echo和sed,轉(zhuǎn)義符往往會(huì)起到相反的效果,
 它反倒有可能引發(fā)出這個(gè)字符特殊的含義.
 
 對(duì)于特定的轉(zhuǎn)義符的特殊的含義
 在echo和sed中所使用的
 \n 意味著新的一行
 \r 回車
 \t tab鍵
 \v vertical tab(垂直tab),查前邊的Ctl-K
 \b backspace,查前邊的Ctl-H
 \a "alert"(如beep或flash)
 \0xx 轉(zhuǎn)換成8進(jìn)制ASCII解碼,等價(jià)于oxx
 
 Example 5-2 轉(zhuǎn)義符
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # escaped.sh: 轉(zhuǎn)義符
 3
 4 echo; echo
 5
 6 echo "\v\v\v\v" # 逐字的打印\v\v\v\v .
 7 # 使用-e選項(xiàng)的echo命令來打印轉(zhuǎn)義符
 8 echo "============="
 9 echo "VERTICAL TABS"
 10 echo -e "\v\v\v\v" # Prints 4 vertical tabs.
 11 echo "=============="
 12
 13 echo "QUOTATION MARK"
 14 echo -e "\042" # 打印" (引號(hào), 8進(jìn)制的ASCII 碼就是42).
 15 echo "=============="
 16
 17 # The $'\X' construct makes the -e option unnecessary.
 17 # 如果使用$'\X'結(jié)構(gòu),那-e選項(xiàng)就不必要了
 18 echo; echo "NEWLINE AND BEEP"
 19 echo $'\n' # 新行.
 20 echo $'\a' # Alert (beep).
 21
 22 echo "==============="
 23 echo "QUOTATION MARKS"
 24 # 版本2以后Bash允許使用$'\nnn'結(jié)構(gòu)
 25 # 注意這種情況,'\nnn\是8進(jìn)制
 26 echo $'\t \042 \t' # Quote (") framed by tabs.
 27
 28 # 當(dāng)然,也可以使用16進(jìn)制的值,使用$'\xhhh' 結(jié)構(gòu)
 29 echo $'\t \x22 \t' # Quote (") framed by tabs.
 30
 31 # 早一點(diǎn)的Bash版本允許'\x022'這種形式
 32 echo "==============="
 33 echo
 34
 35
 36 # 分配ASCII字符到變量中
 37 # ---------------------
 38 quote=$'\042' # \042是",分配到變量中
 39 echo "$quote This is a quoted string, $quote and this lies outside the quotes."
 40
 41 echo
 42
 43 # Concatenating ASCII chars in a variable.
 43 # 變量中的連續(xù)的ASCII char.
 44 triple_underline=$'\137\137\137' # 137 是8進(jìn)制的ASCII 碼'_'.
 45 echo "$triple_underline UNDERLINE $triple_underline"
 46
 47 echo
 48
 49 ABC=$'\101\102\103\010' # 101, 102, 103 是8進(jìn)制的碼A, B, C.
 50 echo $ABC
 51
 52 echo; echo
 53
 54 escape=$'\033' # 033 是8進(jìn)制碼for escape.
 55 echo "\"escape\" echoes as $escape"
 56 #"escape" echoes as 沒有變量被輸出
 57
 58 echo; echo
 59
 60 exit 0
 ################################End Script#########################################
 另一個(gè)關(guān)于$''字符串?dāng)U展結(jié)果的例子見Example 34-1
 
 \" 表達(dá)引號(hào)本身
 1 echo "Hello" # Hello
 2 echo "\"Hello\", he said." # "Hello", he said.
 
 \$ $號(hào)本身,跟在\$后的變量名,將不能擴(kuò)展
 1 echo "\$variable01" # 結(jié)果是$variable01
 
 \\ \號(hào)本身.
 1 echo "\\" # 結(jié)果是\
 2
 3 # 相反的 . . .
 4
 5 echo "\" # 這會(huì)出現(xiàn)第2個(gè)命令提示符,說白了就是提示你命令不全,你再補(bǔ)個(gè)"就
 6 # 好了.如果是在腳本里,就會(huì)給出一個(gè)錯(cuò)誤.
 
 注意:\的行為依賴于它是否被轉(zhuǎn)義,被"",或者是否在"命令替換"和"here document"中.
 ################################Start Script#######################################
 1 # 簡單的轉(zhuǎn)義和""
 2 echo \z # z
 3 echo \\z # \z
 4 echo '\z' # \z
 5 echo '\\z' # \\z
 6 echo "\z" # \z
 7 echo "\\z" # \z
 8
 9 # 命令替換
 10 echo `echo \z` # z
 11 echo `echo \\z` # z
 12 echo `echo \\\z` # \z
 13 echo `echo \\\\z` # \z
 14 echo `echo \\\\\\z` # \z
 15 echo `echo \\\\\\\z` # \\z
 16 echo `echo "\z"` # \z
 17 echo `echo "\\z"` # \z
 18
 19 # Here document
 20 cat <<EOF
 21 \z
 22 EOF # \z
 23
 24 cat <<EOF
 25 \\z
 26 EOF # \z
 ################################End Script#########################################
 
 分配給變量的字符串的元素也會(huì)被轉(zhuǎn)義,但是只把一個(gè)轉(zhuǎn)義符分配給變量將會(huì)報(bào)錯(cuò).
 ################################Start Script#######################################
 1 variable=\
 2 echo "$variable"
 3 # Will not work - gives an error message:
 3 # 將不能正常工作- 將給出一個(gè)錯(cuò)誤消息:
 4 # test.sh: : command not found
 5 # 一個(gè)"裸體的" 轉(zhuǎn)義符將不能夠安全的分配給變量.
 6 #
 7 # What actually happens here is that the "\" escapes the newline and
 7 # 這里其實(shí)真正發(fā)生的是variable=\,這句被shell認(rèn)為是沒有完成,\被認(rèn)為是一個(gè)續(xù)行符
 8 #+ 這樣,下邊的這句echo,也被認(rèn)為是上一行的補(bǔ)充.所以,總的來說就是一個(gè)非法變量分配
 9
 10 variable=\
 11 23skidoo
 12 echo "$variable" # 23skidoo
 13 # 這句就可以使用,因?yàn)檫@是一個(gè)合法的變量分配
 14
 15 variable=\
 16 # \^ 轉(zhuǎn)義一個(gè)空格
 17 echo "$variable" # 顯示空格
 18
 19 variable=\\
 20 echo "$variable" # \
 21
 22 variable=\\\
 23 echo "$variable"
 24 # 不能正常工作,給出一個(gè)錯(cuò)誤
 25 # test.sh: \: command not found
 26 #
 27 # 第一個(gè)轉(zhuǎn)義符把第2個(gè)\轉(zhuǎn)義了,但是第3個(gè)又變成"裸體的"了,
 28 #+ 與上邊的例子的原因相同
 29
 30 variable=\\\\
 31 echo "$variable" # \\
 32 # 轉(zhuǎn)了兩個(gè)\
 33 # 沒問題
 ################################End Script#########################################
 
 轉(zhuǎn)義一個(gè)空格,在命令行參數(shù)列表中將會(huì)阻止單詞分隔問題.
 ################################Start Script#######################################
 1 file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"
 2 # 列出的文件都作為命令的參數(shù).
 3
 4 # Add two files to the list, and list all.
 4 # 加2個(gè)文件到list中,并且列出全部.
 5 ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list
 6
 7 echo "-------------------------------------------------------------------------"
 8
 9 # 如果我們轉(zhuǎn)義2個(gè)空格,會(huì)發(fā)生什么?
 10 ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
 11 # 錯(cuò)誤: 因?yàn)榍?個(gè)路徑名被合并成一個(gè)參數(shù)傳給了'ls -l'
 12 # 因?yàn)?個(gè)轉(zhuǎn)義符阻止了參數(shù)(單詞)分離
 ################################End Script#########################################
 
 轉(zhuǎn)義符也提供續(xù)行功能.一般,每一行都包含一個(gè)不同的命令,但如果在行尾加上\,那就會(huì)接受
 新行的輸入,作為這一行的補(bǔ)充.
 1 (cd /source/directory && tar cf - . ) | \
 2 (cd /dest/directory && tar xpvf -)
 3 # 重復(fù)了 Alan Cox的目錄樹拷貝命令
 4 # 為了增加可讀性分成2行.
 5
 6 # 也可以使用如下方式:
 7 tar cf - -C /source/directory . |
 8 tar xpvf - -C /dest/directory
 9 # 察看下邊的注意事項(xiàng)
 
 注意:如果一個(gè)腳本以|(管道字符)結(jié)束.那么一個(gè)\(轉(zhuǎn)義符),就不用非加上不可了.
 但是一個(gè)好的shell腳本編寫風(fēng)格,還是應(yīng)該在行尾加上\,以增加可讀性.
 ################################Start Script#######################################
 1 echo "foo
 2 bar"
 3 #foo
 4 #bar
 5
 6 echo
 7
 8 echo 'foo
 9 bar' # 沒區(qū)別
 10 #foo
 11 #bar
 12
 13 echo
 14
 15 echo foo\
 16 bar # 續(xù)行
 17 #foobar
 18
 19 echo
 20
 21 echo "foo\
 22 bar" # 與上邊一樣,\還是作為續(xù)行符
 23 #foobar
 24
 25 echo
 26
 27 echo 'foo\
 28 bar' # 由于是強(qiáng)引用,所以\沒被解釋成續(xù)行符
 29 #foo\
 30 #bar
 ################################End Script#########################################
 
 
 
 第6章 退出和退出狀態(tài)
 ====================
 exit命令被用來結(jié)束腳本,就像C語言一樣.他也會(huì)返回一個(gè)值來傳給父進(jìn)程,父進(jìn)程會(huì)判斷是否
 可用.
 
 每個(gè)命令都會(huì)返回一個(gè)exit狀態(tài)(有時(shí)候也叫return狀態(tài)).成功返回0,如果返回一個(gè)非0值,通
 常情況下都會(huì)被認(rèn)為是一個(gè)錯(cuò)誤碼.一個(gè)編寫良好的UNIX命令,程序,和工具都會(huì)返回一個(gè)0作為
 退出碼來表示成功,雖然偶爾也會(huì)有例外.
 
 同樣的,腳本中的函數(shù)和腳本本身都會(huì)返回退出狀態(tài).在腳本或者是腳本函數(shù)中執(zhí)行的最后的命
 令會(huì)決定退出狀態(tài).在腳本中,exit nnn命令將會(huì)把nnn退出碼傳遞給shell(nnn必須是10進(jìn)制數(shù)
 0-255).
 
 當(dāng)一個(gè)腳本以不帶參數(shù)exit來結(jié)束時(shí),腳本的退出狀態(tài)就由腳本中最后執(zhí)行命令來決定.
 1 #!/bin/bash
 2
 3 COMMAND_1
 4
 5 . . .
 6
 7 # 將以最后的命令來決定退出狀態(tài)
 8 COMMAND_LAST
 9
 10 exit $?
 
 1 #!/bin/bash
 2
 3 COMMAND1
 4
 5 . . .
 6
 7 # 將以最后的命令來決定退出狀態(tài)
 8 COMMAND_LAST
 
 $?讀取最后執(zhí)行命令的退出碼.函數(shù)返回后,$?給出函數(shù)最后執(zhí)行的那條命令的退出碼.這種給
 函數(shù)返回值的方法是Bash的方法.對(duì)于腳本來說也一樣.總之,一般情況下,0為成功,非0失敗W.
 Example 6-1 exit/exit狀態(tài)
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 echo hello
 4 echo $? # 返回0,因?yàn)閳?zhí)行成功
 5
 6 lskdf # 不認(rèn)識(shí)的命令.
 7 echo $? # 返回非0值,因?yàn)槭×?
 8
 9 echo
 10
 11 exit 113 # 將返回113給shell.
 12 # To verify this, type "echo $?" after script terminates.
 12 # 為了驗(yàn)證這個(gè),在腳本結(jié)束的地方使用"echo $?"
 ################################End Script#########################################
 
 $?對(duì)于測(cè)試腳本中的命令的結(jié)果特別有用(見Example 12-32和Example 12-17).
 注意: !邏輯非操作,將會(huì)反轉(zhuǎn)test命令的結(jié)果,并且這會(huì)影響exit狀態(tài).
 Example 6-2 否定一個(gè)條件使用!
 ################################Start Script#######################################
 1 true # true是shell內(nèi)建命令,什么事都不做,就是shell返回0
 2 echo "exit status of \"true\" = $?" # 0
 3
 4 ! true
 5 echo "exit status of \"! true\" = $?" # 1
 6 # 注意:"!"需要一個(gè)空格
 7 # !true 將導(dǎo)致一個(gè)"command not found"錯(cuò)誤
 8 #
 9 # 如果一個(gè)命令以'!'開頭,那么將使用Bash的歷史機(jī)制.就是顯示這個(gè)命令被使用的歷史.
 10
 11 true
 12 !true
 13 # 這次就沒有錯(cuò)誤了.
 14 # 他不過是重復(fù)了之前的命令(true).
 ################################End Script#########################################
 
 注意事項(xiàng):
 特定的退出碼都有預(yù)定的含義(見附錄D),用戶不應(yīng)該在自己的腳本中指定他.
 
 
 
 第7章 Tests
 ===========
 每個(gè)完整的合理的編程語言都具有條件判斷的功能.Bash具有test命令,不同的[]和()操作,和
 if/then結(jié)構(gòu).
 
 
 7.1 Test結(jié)構(gòu)
 ------------
 一個(gè)if/then結(jié)構(gòu)可以測(cè)試命令的返回值是否為0(因?yàn)?表示成功),如果是的話,執(zhí)行更多命令.
 
 有一個(gè)專用命令"["(左中括號(hào),特殊字符).這個(gè)命令與test命令等價(jià),但是出于效率上的考慮,
 它是一個(gè)內(nèi)建命令.這個(gè)命令把它的參數(shù)作為比較表達(dá)式或是文件測(cè)試,并且根據(jù)比較的結(jié)果,
 返回一個(gè)退出碼.
 
 在版本2.02的Bash中,推出了一個(gè)新的[[...]]擴(kuò)展test命令.因?yàn)檫@種表現(xiàn)形式可能對(duì)某些語
 言的程序員來說更加熟悉.注意"[["是一個(gè)關(guān)鍵字,并不是一個(gè)命令.
 
 Bash把[[ $a -lt $b ]]看作一個(gè)單獨(dú)的元素,并且返回一個(gè)退出碼.
 
 ((...))和let...結(jié)果也能夠返回一個(gè)退出碼,當(dāng)它們所測(cè)試的算術(shù)表達(dá)式的結(jié)果為非0的時(shí)候,
 他們的退出碼將返回0.這些算術(shù)擴(kuò)展(見第15章)結(jié)構(gòu)被用來做算術(shù)比較.
 1 let "1<2" returns 0 (as "1<2" expands to "1")
 2 (( 0 && 1 )) returns 1 (as "0 && 1" expands to "0")
 
 if命令可以測(cè)試任何命令,不僅僅是括號(hào)中的條件.
 1 if cmp a b &> /dev/null # 阻止輸出.
 2 then echo "Files a and b are identical."
 3 else echo "Files a and b differ."
 4 fi
 5
 6 # 非常有用的"if-grep" 結(jié)構(gòu):
 7 # ------------------------
 8 if grep -q Bash file
 9 then echo "File contains at least one occurrence of Bash."
 10 fi
 11
 12 word=Linux
 13 letter_sequence=inu
 14 if echo "$word" | grep -q "$letter_sequence"
 15 # "-q"選項(xiàng)是用來阻止輸出
 16 then
 17 echo "$letter_sequence found in $word"
 18 else
 19 echo "$letter_sequence not found in $word"
 20 fi
 21
 22
 23 if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED
 24 then echo "Command succeeded."
 25 else echo "Command failed."
 26 fi
 
 一個(gè)if/then結(jié)構(gòu)可以包含多級(jí)比較和tests.
 
 1 if echo "Next *if* is part of the comparison for the first *if*."
 2
 3 if [[ $comparison = "integer" ]]
 4 then (( a < b ))
 5 else
 6 [[ $a < $b ]]
 7 fi
 8
 9 then
 10 echo '$a is less than $b'
 11 fi
 
 Example 7-1 什么情況下為真?
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 # 技巧:
 4 # 如果你不確定一個(gè)特定的條件如何判斷.
 5 #+ 在一個(gè)if-test結(jié)構(gòu)中測(cè)試它.
 6
 7 echo
 8
 9 echo "Testing \"0\""
 10 if [ 0 ] # zero
 11 then
 12 echo "0 is true."
 13 else
 14 echo "0 is false."
 15 fi # 0 is true.
 16
 17 echo
 18
 19 echo "Testing \"1\""
 20 if [ 1 ] # one
 21 then
 22 echo "1 is true."
 23 else
 24 echo "1 is false."
 25 fi # 1 is true.
 26
 27 echo
 28
 29 echo "Testing \"-1\""
 30 if [ -1 ] # -1
 31 then
 32 echo "-1 is true."
 33 else
 34 echo "-1 is false."
 35 fi # -1 is true.
 36
 37 echo
 38
 39 echo "Testing \"NULL\""
 40 if [ ] # NULL (控狀態(tài))
 41 then
 42 echo "NULL is true."
 43 else
 44 echo "NULL is false."
 45 fi # NULL is false.
 46
 47 echo
 48
 49 echo "Testing \"xyz\""
 50 if [ xyz ] # 字符串
 51 then
 52 echo "Random string is true."
 53 else
 54 echo "Random string is false."
 55 fi # Random string is true.
 56
 57 echo
 58
 59 echo "Testing \"\$xyz\""
 60 if [ $xyz ] # 測(cè)試$xyz是否為null,但是...(明顯沒人定義么!)
 61 # 只不過是一個(gè)未定義的變量
 62 then
 63 echo "Uninitialized variable is true."
 64 else
 65 echo "Uninitialized variable is false."
 66 fi # Uninitialized variable is false.
 67
 68 echo
 69
 70 echo "Testing \"-n \$xyz\""
 71 if [ -n "$xyz" ] # 更學(xué)究的的檢查
 72 then
 73 echo "Uninitialized variable is true."
 74 else
 75 echo "Uninitialized variable is false."
 76 fi # Uninitialized variable is false.
 77
 78 echo
 79
 80
 81 xyz= # 初始化了,但是將其設(shè)為空值
 82
 83 echo "Testing \"-n \$xyz\""
 84 if [ -n "$xyz" ]
 85 then
 86 echo "Null variable is true."
 87 else
 88 echo "Null variable is false."
 89 fi # Null variable is false.
 90
 91
 92 echo
 93
 94
 95 # 什么時(shí)候"flase"為true?
 96
 97 echo "Testing \"false\""
 98 if [ "false" ] # 看起來"false"只不過是個(gè)字符串而已.
 99 then
 100 echo "\"false\" is true." #+ 并且它test的結(jié)果就是true.
 101 else
 102 echo "\"false\" is false."
 103 fi # "false" is true.
 104
 105 echo
 106
 107 echo "Testing \"\$false\"" # 再來一個(gè),未聲明的變量
 108 if [ "$false" ]
 109 then
 110 echo "\"\$false\" is true."
 111 else
 112 echo "\"\$false\" is false."
 113 fi # "$false" is false.
 114 # 現(xiàn)在我們終于得到了期望的結(jié)果
 115
 116 # 如果我們test這個(gè)變量"$true"會(huì)發(fā)生什么結(jié)果?答案是和"$flase"一樣,都為空,因?yàn)槲?br /> 117 #+ 們并沒有定義它.
 118 echo
 119
 120 exit 0
 ################################End Script#########################################
 練習(xí).解釋上邊例子的行為(我想我解釋的已經(jīng)夠清楚了)
 
 1 if [ condition-true ]
 2 then
 3 command 1
 4 command 2
 5 ...
 6 else
 7 # 可選的(如果不需要可以省去)
 8 # 如果原始的條件測(cè)試結(jié)果是false,那么添加默認(rèn)的代碼來執(zhí)行.
 9 command 3
 10 command 4
 11 ...
 12 fi
 
 注意:當(dāng)if和then在一個(gè)條件測(cè)試的同一行中的話,必須使用";"來終止if表達(dá)式.if和then都是
 關(guān)鍵字.關(guān)鍵字(或者命令)作為一個(gè)表達(dá)式的開頭,并且在一個(gè)新的表達(dá)式開始之前,必須
 結(jié)束上一個(gè)表達(dá)式.
 1 if [ -x "$filename" ]; then
 
 Else if和elif
 
 elif
 elif是else if的縮減形式.
 1 if [ condition1 ]
 2 then
 3 command1
 4 command2
 5 command3
 6 elif [ condition2 ]
 7 # Same as else if
 8 then
 9 command4
 10 command5
 11 else
 12 default-command
 13 fi
 
 使用if test condition-true這種形式和if[condition-true]這種形式是等價(jià)的.向我們前邊
 所說的"["是test的標(biāo)記.并且以"]"結(jié)束.在if/test中并不應(yīng)該這么嚴(yán)厲,但是新版本的Bash
 需要它.
 
 注意:test命令是Bash的內(nèi)建命令,用來測(cè)試文件類型和比較字符串.因此,在Bash腳本中,test
 并不調(diào)用/usr/bin/test的二進(jìn)制版本(這是sh-utils工具包的一部分).同樣的,[并不調(diào)用
 /usr/bin/[,被連接到/usr/bin/test.
 bash$ type test
 test is a shell builtin
 bash$ type '['
 [ is a shell builtin
 bash$ type '[['
 [[ is a shell keyword
 bash$ type ']]'
 ]] is a shell keyword
 bash$ type ']'
 bash: type: ]: not found
 
 Example 7-2 幾個(gè)等效命令test,/usr/bin/test,[],和/usr/bin/[
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 echo
 4
 5 if test -z "$1"
 6 then
 7 echo "No command-line arguments."
 8 else
 9 echo "First command-line argument is $1."
 10 fi
 11
 12 echo
 13
 14 if /usr/bin/test -z "$1" # 與內(nèi)建的test結(jié)果相同
 15 then
 16 echo "No command-line arguments."
 17 else
 18 echo "First command-line argument is $1."
 19 fi
 20
 21 echo
 22
 23 if [ -z "$1" ] # 與上邊代碼的作用相同
 24 # if [ -z "$1" 應(yīng)該工作,但是...
 25 #+ Bash相應(yīng)一個(gè)缺少關(guān)閉中括號(hào)的錯(cuò)誤消息.
 26 then
 27 echo "No command-line arguments."
 28 else
 29 echo "First command-line argument is $1."
 30 fi
 31
 32 echo
 33
 34
 35 if /usr/bin/[ -z "$1" ] # 再來一個(gè),與上邊代碼的作用相同
 36 # if /usr/bin/[ -z "$1" # 工作,但是給個(gè)錯(cuò)誤消息
 37 # # 注意:
 38 # This has been fixed in Bash, version 3.x.
 38 # 在ver 3.x上,這個(gè)bug已經(jīng)被Bash修正了.
 39 then
 40 echo "No command-line arguments."
 41 else
 42 echo "First command-line argument is $1."
 43 fi
 44
 45 echo
 46
 47 exit 0
 ###############################End Script#########################################
 
 [[]]結(jié)構(gòu)比Bash的[]更加靈活,這是一個(gè)擴(kuò)展的test命令,從ksh88繼承過來的.
 注意:在[[]]結(jié)構(gòu)中,將沒有文件擴(kuò)展或者是單詞分離,但是會(huì)發(fā)生參數(shù)擴(kuò)展和命令替換.
 1 file=/etc/passwd
 2
 3 if [[ -e $file ]]
 4 then
 5 echo "Password file exists."
 6 fi
 注意:使用[[]],而不是[],能夠阻止腳本中的許多邏輯錯(cuò)誤.比如,盡管在[]中將給出一個(gè)錯(cuò)誤,
 但是&&,||,<>操作還是能夠工作在一個(gè)[[]]test之中.
 注意:在if后邊,test命令和[]或[[]]都不是必須的.如下:
 1 dir=/home/bozo
 2
 3 if cd "$dir" 2>/dev/null; then # "2>/dev/null" hides error message.
 4 echo "Now in $dir."
 5 else
 6 echo "Can't change to $dir."
 7 fi
 if命令將返回if后邊的命令的退出碼.
 
 與此相似,當(dāng)在一個(gè)在使用與或列表結(jié)構(gòu)的時(shí)候,test或中括號(hào)的使用,也并不一定非的有if不可
 1 var1=20
 2 var2=22
 3 [ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"
 4
 5 home=/home/bozo
 6 [ -d "$home" ] || echo "$home directory does not exist."
 
 (())結(jié)構(gòu)擴(kuò)展并計(jì)算一個(gè)算術(shù)表達(dá)式的結(jié)果.如果表達(dá)式的結(jié)果為0,它將返回1作為退出碼,或
 者是"false".而一個(gè)非0表達(dá)式的結(jié)果將返回0作為退出碼,或者是"true".
 
 Example 7-3 算數(shù)測(cè)試使用(( ))
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # 算數(shù)測(cè)試
 3
 4 # The (( ... )) construct evaluates and tests numerical expressions.
 4 # (( ... ))結(jié)構(gòu)計(jì)算并測(cè)試算數(shù)表達(dá)式的結(jié)果.
 5 # 退出碼將與[ ... ]結(jié)構(gòu)相反!
 6
 7 (( 0 ))
 8 echo "Exit status of \"(( 0 ))\" is $?." # 1
 9
 10 (( 1 ))
 11 echo "Exit status of \"(( 1 ))\" is $?." # 0
 12
 13 (( 5 > 4 )) # true
 14 echo "Exit status of \"(( 5 > 4 ))\" is $?." # 0
 15
 16 (( 5 > 9 )) # false
 17 echo "Exit status of \"(( 5 > 9 ))\" is $?." # 1
 18
 19 (( 5 - 5 )) # 0
 20 echo "Exit status of \"(( 5 - 5 ))\" is $?." # 1
 21
 22 (( 5 / 4 )) # 除法也行
 23 echo "Exit status of \"(( 5 / 4 ))\" is $?." # 0
 24
 25 (( 1 / 2 )) # 出發(fā)結(jié)果<1
 26 echo "Exit status of \"(( 1 / 2 ))\" is $?." # 結(jié)果將為0
 27 # 1
 28
 29 (( 1 / 0 )) 2>/dev/null # 除數(shù)為0的錯(cuò)誤
 30 # ^^^^^^^^^^^
 31 echo "Exit status of \"(( 1 / 0 ))\" is $?." # 1
 32
 33 # What effect does the "2>/dev/null" have?
 33 # "2>/dev/null"的作用是什么?
 34 # 如果刪除"2>dev/null"將會(huì)發(fā)生什么?
 35 # Try removing it, then rerunning the script.
 35 # 嘗試刪除它,然后再運(yùn)行腳本.
 36
 37 exit 0
 ################################End Script#########################################
 
 
 7.2 文件測(cè)試操作
 ----------------
 返回true如果...
 
 -e 文件存在
 -a 文件存在
 這個(gè)選項(xiàng)的效果與-e相同.但是它已經(jīng)被棄用了,并且不鼓勵(lì)使用
 -f file是一個(gè)regular文件(不是目錄或者設(shè)備文件)
 -s 文件長度不為0
 -d 文件是個(gè)目錄
 -b 文件是個(gè)塊設(shè)備(軟盤,cdrom等等)
 -c 文件是個(gè)字符設(shè)備(鍵盤,modem,聲卡等等)
 -p 文件是個(gè)管道
 -h 文件是個(gè)符號(hào)鏈接
 -L 文件是個(gè)符號(hào)鏈接
 -S 文件是個(gè)socket
 -t 關(guān)聯(lián)到一個(gè)終端設(shè)備的文件描述符
 這個(gè)選項(xiàng)一般都用來檢測(cè)是否在一個(gè)給定腳本中的stdin[-t0]或[-t1]是一個(gè)終端
 -r 文件具有讀權(quán)限(對(duì)于用戶運(yùn)行這個(gè)test)
 -w 文件具有寫權(quán)限(對(duì)于用戶運(yùn)行這個(gè)test)
 -x 文件具有執(zhí)行權(quán)限(對(duì)于用戶運(yùn)行這個(gè)test)
 -g set-group-id(sgid)標(biāo)志到文件或目錄上
 如果一個(gè)目錄具有sgid標(biāo)志,那么一個(gè)被創(chuàng)建在這個(gè)目錄里的文件,這個(gè)目錄屬于創(chuàng)建
 這個(gè)目錄的用戶組,并不一定與創(chuàng)建這個(gè)文件的用戶的組相同.對(duì)于workgroup的目錄
 共享來說,這非常有用.見<>第58頁.
 -u set-user-id(suid)標(biāo)志到文件上
 如果運(yùn)行一個(gè)具有root權(quán)限的文件,那么運(yùn)行進(jìn)程將取得root權(quán)限,即使你是一個(gè)普通
 用戶.[1]這對(duì)于需要存取系統(tǒng)硬件的執(zhí)行操作(比如pppd和cdrecord)非常有用.如果
 沒有suid標(biāo)志的話,那么普通用戶(沒有root權(quán)限)將無法運(yùn)行這種程序.
 見<>第58頁.
 -rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd
 對(duì)于設(shè)置了suid的文件,在它的權(quán)限標(biāo)志中有"s".
 -k 設(shè)置粘貼位,見<>第65頁.
 對(duì)于"sticky bit",save-text-mode標(biāo)志是一個(gè)文件權(quán)限的特殊類型.如果設(shè)置了這
 個(gè)標(biāo)志,那么這個(gè)文件將被保存在交換區(qū),為了達(dá)到快速存取的目的.如果設(shè)置在目錄
 中,它將限制寫權(quán)限.對(duì)于設(shè)置了sticky bit位的文件或目錄,權(quán)限標(biāo)志中有"t".
 drwxrwxrwt 7 root 1024 May 19 21:26 tmp/
 如果一個(gè)用戶并不時(shí)具有stick bit位的目錄的擁有者,但是具有寫權(quán)限,那么用戶只
 能在這個(gè)目錄下刪除自己所擁有的文件.這將防止用戶在一個(gè)公開的目錄中不慎覆蓋
 或者刪除別人的文件,比如/tmp(當(dāng)然root或者是目錄的所有者可以隨便刪除或重命名
 其中的文件).
 -O 你是文件的所有者.
 -G 文件的group-id和你的相同.
 -N 從文件最后被閱讀到現(xiàn)在,是否被修改.
 
 f1 -nt f2
 文件f1比f2新
 f1 -ot f2
 f1比f2老
 f1 -ef f2
 f1和f2都硬連接到同一個(gè)文件.
 
 ! 非--反轉(zhuǎn)上邊測(cè)試的結(jié)果(如果條件缺席,將返回true)
 
 Example 7-4 test死的鏈接文件
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # broken-link.sh
 3 # Written by Lee bigelow <ligelowbee@yahoo.com>
 4 # Used with permission.
 5
 6 #一個(gè)真正有用的shell腳本來找出死鏈接文件并且輸出它們的引用
 7 #以便于它們可以被輸入到xargs命令中進(jìn)行處理 :)
 8 #比如: broken-link.sh /somedir /someotherdir|xargs rm
 9 #
 10 #這里,不管怎么說,是一種更好的方法
 11 #
 12 #find "somedir" -type l -print0|\
 13 #xargs -r0 file|\
 14 #grep "broken symbolic"|
 15 #sed -e 's/^\|: *broken symbolic.*$/"/g'
 16 #
 17 #但這不是一個(gè)純粹的bash,最起碼現(xiàn)在不是.
 18 #小心:小心/proc文件系統(tǒng)和任何的循環(huán)鏈接文件.
 19 ##############################################################
 20
 21
 22 #如果沒對(duì)這個(gè)腳本傳遞參數(shù),那么就使用當(dāng)前目錄.
 23 #否則就使用傳遞進(jìn)來的參數(shù)作為目錄來搜索.
 24 #
 25 ####################
 26 [ $# -eq 0 ] && directorys=`pwd` || directorys=$@
 27
 28 #建立函數(shù)linkchk來檢查傳進(jìn)來的目錄或文件是否是鏈接和是否存在,
 29 #并且打印出它們的引用
 30 #如果傳進(jìn)來的目錄有子目錄,
 31 #那么把子目錄也發(fā)送到linkchk函數(shù)中處理,就是遞歸目錄.
 32 ##########
 33 linkchk () {
 34 for element in $1/*; do
 35 [ -h "$element" -a ! -e "$element" ] && echo \"$element\"
 36 [ -d "$element" ] && linkchk $element
 37 # Of course, '-h' tests for symbolic link, '-d' for directory.
 37 # 當(dāng)然'-h'是測(cè)試鏈接,'-d'是測(cè)試目錄.
 38 done
 39 }
 40
 41 #如果是個(gè)可用目錄,那就把每個(gè)從腳本傳遞進(jìn)來的參數(shù)都送到linkche函數(shù)中.
 42 #如果不是,那就打印出錯(cuò)誤消息和使用信息.
 43 #
 44 ################
 45 for directory in $directorys; do
 46 if [ -d $directory ]
 47 then linkchk $directory
 48 else
 49 echo "$directory is not a directory"
 50 echo "Usage: $0 dir1 dir2 ..."
 51 fi
 52 done
 53
 54 exit 0
 ################################End Script#########################################
 Example 28-1, Example 10-7, Example 10-3, Example 28-3, 和Example A-1 也會(huì)說明文件
 測(cè)試操作的使用過程.
 
 注意事項(xiàng):
 [1] 小心suid,可能引起安全漏洞,但是不會(huì)影響shell腳本.
 [2] 在當(dāng)代UNIX系統(tǒng)中,已經(jīng)不使用sticky bit了,只在目錄中使用.
 
 
 7.3 其他比較操作
 ----------------
 二元比較操作符,比較變量或者比較數(shù)字.注意數(shù)字與字符串的區(qū)別.
 
 整數(shù)比較
 
 -eq 等于,如:if [ "$a" -eq "$b" ]
 -ne 不等于,如:if [ "$a" -ne "$b" ]
 -gt 大于,如:if [ "$a" -gt "$b" ]
 -ge 大于等于,如:if [ "$a" -ge "$b" ]
 -lt 小于,如:if [ "$a" -lt "$b" ]
 -le 小于等于,如:if [ "$a" -le "$b" ]
 < 小于(需要雙括號(hào)),如:(("$a" < "$b"))
 <= 小于等于(需要雙括號(hào)),如:(("$a" <= "$b"))
 > 大于(需要雙括號(hào)),如:(("$a" > "$b"))
 >= 大于等于(需要雙括號(hào)),如:(("$a" >= "$b"))
 
 字符串比較
 = 等于,如:if [ "$a" = "$b" ]
 == 等于,如:if [ "$a" == "$b" ],與=等價(jià)
 注意:==的功能在[[]]和[]中的行為是不同的,如下:
 1 [[ $a == z* ]] # 如果$a以"z"開頭(模式匹配)那么將為true
 2 [[ $a == "z*" ]] # 如果$a等于z*(字符匹配),那么結(jié)果為true
 3
 4 [ $a == z* ] # File globbing 和word splitting將會(huì)發(fā)生
 5 [ "$a" == "z*" ] # 如果$a等于z*(字符匹配),那么結(jié)果為true
 一點(diǎn)解釋,關(guān)于File globbing是一種關(guān)于文件的速記法,比如"*.c"就是,再如~也是.
 但是file globbing并不是嚴(yán)格的正則表達(dá)式,雖然絕大多數(shù)情況下結(jié)構(gòu)比較像.
 != 不等于,如:if [ "$a" != "$b" ]
 這個(gè)操作符將在[[]]結(jié)構(gòu)中使用模式匹配.
 < 小于,在ASCII字母順序下.如:
 if [[ "$a" < "$b" ]]
 if [ "$a" \< "$b" ]
 注意:在[]結(jié)構(gòu)中"<"需要被轉(zhuǎn)義.
 > 大于,在ASCII字母順序下.如:
 if [[ "$a" > "$b" ]]
 if [ "$a" \> "$b" ]
 注意:在[]結(jié)構(gòu)中">"需要被轉(zhuǎn)義.
 具體參考Example 26-11來查看這個(gè)操作符應(yīng)用的例子.
 -z 字符串為"null".就是長度為0.
 -n 字符串不為"null"
 注意:
 使用-n在[]結(jié)構(gòu)中測(cè)試必須要用""把變量引起來.使用一個(gè)未被""的字符串來使用! -z
 或者就是未用""引用的字符串本身,放到[]結(jié)構(gòu)中(見Example 7-6)雖然一般情況下可
 以工作,但這是不安全的.習(xí)慣于使用""來測(cè)試字符串是一種好習(xí)慣.[1]
 
 Example 7-5 數(shù)字和字符串比較
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 a=4
 4 b=5
 5
 6 # 這里的變量a和b既可以當(dāng)作整型也可以當(dāng)作是字符串.
 7 # 這里在算術(shù)比較和字符串比較之間有些混淆,
 8 #+ 因?yàn)锽ash變量并不是強(qiáng)類型的.
 9
 10 # Bash允許對(duì)整型變量操作和比較
 11 #+ 當(dāng)然變量中只包含數(shù)字字符.
 12 # 但是還是要考慮清楚再做.
 13
 14 echo
 15
 16 if [ "$a" -ne "$b" ]
 17 then
 18 echo "$a is not equal to $b"
 19 echo "(arithmetic comparison)"
 20 fi
 21
 22 echo
 23
 24 if [ "$a" != "$b" ]
 25 then
 26 echo "$a is not equal to $b."
 27 echo "(string comparison)"
 28 # "4" != "5"
 29 # ASCII 52 != ASCII 53
 30 fi
 31
 32 # 在這個(gè)特定的例子中,"-ne"和"!="都可以.
 33
 34 echo
 35
 36 exit 0
 ################################End Script#########################################
 
 Example 7-6 測(cè)試字符串是否為null
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # str-test.sh: 測(cè)試null字符串和非引用字符串,
 3 #+ but not strings and sealing wax, not to mention cabbages and kings . . .
 4 #+ 上邊這句沒看懂
 5 # Using if [ ... ]
 6
 7
 8 # 如果一個(gè)字符串沒被初始化,那么它就沒有定義的值(像這種話,總感覺像屁話)
 9 # 這種狀態(tài)叫做"null"(與zero不同)
 10
 11 if [ -n $string1 ] # $string1 沒被聲明和初始化
 12 then
 13 echo "String \"string1\" is not null."
 14 else
 15 echo "String \"string1\" is null."
 16 fi
 17 # 錯(cuò)誤的結(jié)果.
 18 # 顯示$string1為非空,雖然他沒被初始化.
 19
 20
 21 echo
 22
 23
 24 # 讓我們?cè)僭囈幌?
 25
 26 if [ -n "$string1" ] # 這次$string1被引用了.
 27 then
 28 echo "String \"string1\" is not null."
 29 else
 30 echo "String \"string1\" is null."
 31 fi # ""的字符串在[]結(jié)構(gòu)中
 32
 33
 34 echo
 35
 36
 37 if [ $string1 ] # 這次$string1變成"裸體"的了
 38 then
 39 echo "String \"string1\" is not null."
 40 else
 41 echo "String \"string1\" is null."
 42 fi
 43 # 這工作得很好.
 44 # 這個(gè)[]test操作檢測(cè)string是否為null.
 45 # 然而,使用("$string1")是一種很好的習(xí)慣
 46 #
 47 # As Stephane Chazelas points out,
 48 # if [ $string1 ] 有1個(gè)參數(shù) "]"
 49 # if [ "$string1" ] 有2個(gè)參數(shù),空的"$string1"和"]"
 50
 51
 52
 53 echo
 54
 55
 56
 57 string1=initialized
 58
 59 if [ $string1 ] # 再來,$string1"裸體了"
 60 then
 61 echo "String \"string1\" is not null."
 62 else
 63 echo "String \"string1\" is null."
 64 fi
 65 # 再來,給出了正確的結(jié)果.
 66 # 不過怎么說("$string1")還是好很多,因?yàn)? . .
 67
 68
 69 string1="a = b"
 70
 71 if [ $string1 ] # 再來,$string1 再次裸體了.
 72 then
 73 echo "String \"string1\" is not null."
 74 else
 75 echo "String \"string1\" is null."
 76 fi
 77 # 非引用的"$string1"現(xiàn)在給出了一個(gè)錯(cuò)誤的結(jié)果!
 78
 79 exit 0
 80 # Thank you, also, Florian Wisser, for the "heads-up".
 ################################End Script#########################################
 
 Example 7-7 zmore
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # zmore
 3
 4 #使用'more'來查看gzip文件
 5
 6 NOARGS=65
 7 NOTFOUND=66
 8 NOTGZIP=67
 9
 10 if [ $# -eq 0 ] # 與 if [ -z "$1" ]同樣的效果
 11 # 應(yīng)該是說前邊的那句注釋有問題,$1是可以存在的,比如:zmore "" arg2 arg3
 12 then
 13 echo "Usage: `basename $0` filename" >&2
 14 # 錯(cuò)誤消息到stderr
 15 exit $NOARGS
 16 # 腳本返回65作為退出碼.
 17 fi
 18
 19 filename=$1
 20
 21 if [ ! -f "$filename" ] # 將$filename ""起來,來允許可能的空白
 22 then
 23 echo "File $filename not found!" >&2
 24 # 錯(cuò)誤消息到stderr
 25 exit $NOTFOUND
 26 fi
 27
 28 if [ ${filename##*.} != "gz" ]
 29 # 在變量替換中使用中括號(hào)
 30 then
 31 echo "File $1 is not a gzipped file!"
 32 exit $NOTGZIP
 33 fi
 34
 35 zcat $1 | more
 36
 37 # 使用過濾命令'more'
 38 # 如果你想的話也可使用'less'
 39
 40
 41 exit $? # 腳本將返回pipe的結(jié)果作為退出碼
 42 # 事實(shí)上,不用非的有"exit $?",但是不管怎么說,有了這句,能正規(guī)一些
 43 # 將最后一句命令的執(zhí)行狀態(tài)作為退出碼返回
 ################################End Script#########################################
 
 混合比較
 
 -a 邏輯與
 exp1 -a exp2 如果exp1和exp2都為true的話,這個(gè)表達(dá)式將返回true
 
 -o 邏輯或
 exp1 -o exp2 如果exp1和exp2中有一個(gè)為true的話,那么這個(gè)表達(dá)式就返回true
 
 這與Bash的比較操作符&&和||很相像.在[[]]中使用它.
 1 [[ condition1 && condition2 ]]
 -o和-a一般都是和test命令或者是[]一起工作.
 1 if [ "$exp1" -a "$exp2" ]
 
 請(qǐng)參考Example 8-3,Example 26-16和Example A-28來查看混合比較操作的行為.
 
 注意事項(xiàng):
 [1] S.C.(這家伙是個(gè)人名)指出,在使用混合比較的時(shí)候即使"$var"也可能會(huì)產(chǎn)生問題.
 如果$string為空的話,[ -n "$string" -o "$a" = "$b" ]可能在某些版本的Bash中
 會(huì)有問題.為了附加一個(gè)額外的字符到可能的空變量中的一種安全的辦法是,
 [ "x$string" != x -o "x$a" = "x$b" ](the "x's" cancel out)(沒看懂).
 cancel out是抵消的意思.
 
 
 7.4 嵌套的if/then條件test
 -------------------------
 可以使用if/then來進(jìn)行嵌套的條件test.最終的結(jié)果和上邊的使用&&混合比較操作是相同的.
 1 if [ condition1 ]
 2 then
 3 if [ condition2 ]
 4 then
 5 do-something # 這里只有在condition1和condition2都可用的時(shí)候才行.
 6 fi
 7 fi
 具體請(qǐng)查看Example 34-4.
 
 
 7.5 檢查你的test知識(shí)
 --------------------
 系統(tǒng)范圍的xinitrc文件可以用來啟動(dòng)X server.這個(gè)文件中包含了相當(dāng)多的if/then test,
 就像下邊的節(jié)選一樣:
 1 if [ -f $HOME/.Xclients ]; then
 2 exec $HOME/.Xclients
 3 elif [ -f /etc/X11/xinit/Xclients ]; then
 4 exec /etc/X11/xinit/Xclients
 5 else
 6 # 故障保險(xiǎn)設(shè)置,雖然我們永遠(yuǎn)都不會(huì)走到這來.
 7 # (我們?cè)赬clients中也提供了相同的機(jī)制)它不會(huì)受傷的.
 8 xclock -geometry 100x100-5+5 &
 9 xterm -geometry 80x50-50+150 &
 10 if [ -f /usr/bin/netscape -a -f /usr/share/doc/HTML/index.html ]; then
 11 netscape /usr/share/doc/HTML/index.html &
 12 fi
 13 fi
 
 對(duì)上邊的"test"結(jié)構(gòu)進(jìn)行解釋,然后檢查整個(gè)文件,/etc/X11/xinit/xinitrc,并分析if/then
 test結(jié)構(gòu).你可能需要查看一下后邊才能講解到的grep,sed和正則表達(dá)式的知識(shí).
 
 
 
 第8章 操作符和相關(guān)的主題
 ========================
 
 8.1 操作符
 ----------
 
 等號(hào)操作符
 
 變量賦值
 初始化或者修改變量的值
 =
 無論在算術(shù)運(yùn)算還是字符串運(yùn)算中,都是賦值語句.
 1 var=27
 2 category=minerals # No spaces allowed after the "=".
 
 注意:不要和"="test操作符混淆.
 1 # = as a test operator
 2
 3 if [ "$string1" = "$string2" ]
 4 # if [ "X$string1" = "X$string2" ] is safer,
 5 # to prevent an error message should one of the variables be empty.
 6 # (The prepended "X" characters cancel out.)
 7 then
 8 command
 9 fi
 
 算術(shù)操作符
 
 + 加法
 - 減法
 * 乘法
 / 除法
 ** 冪運(yùn)算
 1 # Bash, version 2.02, introduced the "**" exponentiation operator.
 2
 3 let "z=5**3"
 4 echo "z = $z" # z = 125
 % 取模
 bash$ expr 5 % 3
 2
 
 5/3=1余2
 模運(yùn)算經(jīng)常用在其它的事情中,比如產(chǎn)生特定的范圍的數(shù)字(Example 9-24,
 Example 9-27)和格式化程序的輸出(Example 26-15,Example A-6).它甚至可以用來
 產(chǎn)生質(zhì)數(shù),(Example A-16).事實(shí)上取模運(yùn)算在算術(shù)運(yùn)算中使用的頻率驚人的高.
 
 Example 8-1 最大公約數(shù)
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # gcd.sh: 最大公約數(shù)
 3 # 使用Euclid's 算法
 4
 5 # 最大公約數(shù),就是2個(gè)數(shù)能夠同時(shí)整除的最大的數(shù).
 6 #
 7
 8 # Euclid's算法采用連續(xù)除法.
 9 # 在每個(gè)循環(huán)中
 10 #+ 被除數(shù) <--- 除數(shù)
 11 #+ 除數(shù) <--- 余數(shù)
 12 #+ 直到余數(shù)= 0.
 13 #+ 在最后的循環(huán)中The gcd = 被除數(shù)
 14 #
 15 # 關(guān)于這個(gè)算法更精彩的討論
 16 # 見Jim Loy's site, http://www.jimloy.com/number/euclids.htm.
 17
 18
 19 # ------------------------------------------------------
 20 # 參數(shù)檢查
 21 ARGS=2
 22 E_BADARGS=65
 23
 24 if [ $# -ne "$ARGS" ]
 25 then
 26 echo "Usage: `basename $0` first-number second-number"
 27 exit $E_BADARGS
 28 fi
 29 # ------------------------------------------------------
 30
 31
 32 gcd ()
 33 {
 34
 35 dividend=$1 # 隨便給值
 36 divisor=$2 #+ 即使$2大,也沒關(guān)系.
 37 # Why not?
 38
 39 remainder=1 # 如果再循環(huán)中使用為初始化的變量.
 40 #+ 那將在第一次循環(huán)中產(chǎn)生一個(gè)錯(cuò)誤消息.
 41
 42
 43 until [ "$remainder" -eq 0 ]
 44 do
 45 let "remainder = $dividend % $divisor"
 46 dividend=$divisor # 現(xiàn)在使用2個(gè)最小的數(shù)重復(fù).
 47 divisor=$remainder
 48 done # Euclid's algorithm
 49
 50 } # Last $dividend is the gcd.
 50 } # 最后的$dividend就是gcd.
 51
 52
 53 gcd $1 $2
 54
 55 echo; echo "GCD of $1 and $2 = $dividend"; echo
 56
 57
 58 # 練習(xí):
 59 # --------
 60 # 檢查命令行參數(shù)來確定它們都是整數(shù),
 61 #+ and exit the script with an appropriate error message if not.
 61 #+ 否則就選擇合適的錯(cuò)誤消息退出.
 62
 63 exit 0
 ################################End Script#########################################
 
 += 加等于(通過常量增加變量)
 let "var += 5" #var將在本身值的基礎(chǔ)上增加5
 -= 減等于
 *= 乘等于
 let "var *= 4"
 /= 除等于
 %= 取模賦值,算術(shù)操作經(jīng)常使用expr或者let表達(dá)式.
 
 Example 8-2 使用算術(shù)操作符
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # Counting to 11 in 10 different ways.
 3
 4 n=1; echo -n "$n "
 5
 6 let "n = $n + 1" # let "n = n + 1" 這么寫也行
 7 echo -n "$n "
 8
 9
 10 : $((n = $n + 1))
 11 # ":" 是必須的,這是因?yàn)?如果沒有":"的話,Bash將
 12 #+ 嘗試把"$((n = $n + 1))"解釋成一個(gè)命令
 13 echo -n "$n "
 14
 15 (( n = n + 1 ))
 16 # 對(duì)于上邊的方法的一個(gè)更簡單的選則.
 17 # Thanks, David Lombard, for pointing this out.
 18 echo -n "$n "
 19
 20 n=$(($n + 1))
 21 echo -n "$n "
 22
 23 : $[ n = $n + 1 ]
 24 # ":" 是必須的,這是因?yàn)?如果沒有":"的話,Bash將
 25 #+ 嘗試把"$[ n = $n + 1 ]" 解釋成一個(gè)命令
 26 # 即使"n"被初始化成為一個(gè)字符串,這句也能工作.
 27 echo -n "$n "
 28
 29 n=$[ $n + 1 ]
 30 # 即使"n"被初始化成為一個(gè)字符串,這句也能工作.
 31 #* Avoid this type of construct, since it is obsolete and nonportable.
 31 #* 盡量避免這種類型的結(jié)果,因?yàn)檫@已經(jīng)被廢棄了,并且不具可移植性.
 32 # Thanks, Stephane Chazelas.
 33 echo -n "$n "
 34
 35 # 現(xiàn)在來個(gè)C風(fēng)格的增量操作.
 36 # Thanks, Frank Wang, for pointing this out.
 37
 38 let "n++" # let "++n" also works.
 39 echo -n "$n "
 40
 41 (( n++ )) # (( ++n ) also works.
 42 echo -n "$n "
 43
 44 : $(( n++ )) # : $(( ++n )) also works.
 45 echo -n "$n "
 46
 47 : $[ n++ ] # : $[ ++n ]] also works
 48 echo -n "$n "
 49
 50 echo
 51
 52 exit 0
 ################################End Script#########################################
 
 注意:在Bash中的整型變量事實(shí)上是32位的,范圍是 -2147483648 到2147483647.如果超過這個(gè)
 范圍進(jìn)行算術(shù)操作,將不會(huì)得到你期望的結(jié)果(就是溢出么).
 1 a=2147483646
 2 echo "a = $a" # a = 2147483646
 3 let "a+=1" # 加1 "a".
 4 echo "a = $a" # a = 2147483647
 5 let "a+=1" # 再加1 "a" ,將超過上限了.
 6 echo "a = $a" # a = -2147483648
 7 # 錯(cuò)誤 (溢出了)
 在Bash 2.05b版本中,Bash支持64位整型了.
 
 注意:Bash并不能理解浮點(diǎn)運(yùn)算.它把包含的小數(shù)點(diǎn)看作字符串.
 1 a=1.5
 2
 3 let "b = $a + 1.3" # 錯(cuò)誤.
 4 # t2.sh: let: b = 1.5 + 1.3: 表達(dá)式的語義錯(cuò)誤(錯(cuò)誤標(biāo)志為".5 + 1.3")
 5
 6 echo "b = $b" # b=1
 如果真想做浮點(diǎn)運(yùn)算的話,使用bc(見12.8節(jié)),bc可以進(jìn)行浮點(diǎn)運(yùn)算或調(diào)用數(shù)學(xué)庫函數(shù).
 
 位操作符.
 (暈,有點(diǎn)強(qiáng)大過分了吧,位級(jí)操作都支持.)
 位操作符在shell腳本中極少使用.它們最主要的用途看起來就是操作和test從sockets中
 讀出的變量."Bit flipping"與編譯語言的聯(lián)系很緊密,比如c/c++,在這種語言中它可以
 運(yùn)行得足夠快.(原文有處on the fly,我查了一下,好像是沒事干的意思,沒理解)
 
 << 左移1位(每次左移都將乘2)
 
 <<= 左移幾位,=號(hào)后邊將給出左移幾位
 let "var <<= 2"就是左移2位(就是乘4)
 
 >> 右移1位(每次右移都將除2)
 
 >>= 右移幾位
 
 & 按位與
 
 &= 按位與賦值
 
 | 按位或
 
 |= 按位或賦值
 
 ~ 按位非
 
 ! 按位否?(沒理解和上邊的~有什么區(qū)別?),感覺是應(yīng)該放到下邊的邏輯操作中
 
 ^ 按位異或XOR
 
 ^= 異或賦值
 
 
 邏輯操作:
 
 && 邏輯與
 1 if [ $condition1 ] && [ $condition2 ]
 2 # 與: if [ $condition1 -a $condition2 ] 相同
 3 # 如果condition1和condition2都為true,那結(jié)果就為true.
 4
 5 if [[ $condition1 && $condition2 ]] # 也可以.
 6 # 注意&&不允許出現(xiàn)在[ ... ]中.
 注意:&&也可以用在and list中(見25章),但是使用的時(shí)候需要依賴上下文.
 
 || 邏輯或
 1 if [ $condition1 ] || [ $condition2 ]
 2 # 與: if [ $condition1 -o $condition2 ] 相同
 3 # 如果condition1或condition2為true,那結(jié)果就為true.
 4
 5 if [[ $condition1 || $condition2 ]] # 也可以
 6 # 注意||不允許出現(xiàn)在[ ... ]中.
 注意:Bash將test每個(gè)連接到邏輯操作的狀態(tài)的退出狀態(tài)(見第6章).
 
 
 Example 8-3 使用&&和||進(jìn)行混合狀態(tài)的test
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 a=24
 4 b=47
 5
 6 if [ "$a" -eq 24 ] && [ "$b" -eq 47 ]
 7 then
 8 echo "Test #1 succeeds."
 9 else
 10 echo "Test #1 fails."
 11 fi
 12
 13 # 錯(cuò)誤: if [ "$a" -eq 24 && "$b" -eq 47 ]
 14 #+ 嘗試執(zhí)行' [ "$a" -eq 24 '
 15 #+ 因?yàn)闆]找到']'所以失敗了.
 16 #
 17 # 注意: 如果 [[ $a -eq 24 && $b -eq 24 ]] 能夠工作.
 18 # 那這個(gè)[[]]的test結(jié)構(gòu)就比[]結(jié)構(gòu)更靈活了.
 19 #
 20 # (在17行的"&&"與第6行的"&&"意義不同)
 21 # Thanks, Stephane Chazelas, for pointing this out.
 22
 23
 24 if [ "$a" -eq 98 ] || [ "$b" -eq 47 ]
 25 then
 26 echo "Test #2 succeeds."
 27 else
 28 echo "Test #2 fails."
 29 fi
 30
 31
 32 # -a和-o選項(xiàng)提供了
 33 #+ 一種可選的混合test方法.
 34 # Thanks to Patrick Callahan for pointing this out.
 35
 36
 37 if [ "$a" -eq 24 -a "$b" -eq 47 ]
 38 then
 39 echo "Test #3 succeeds."
 40 else
 41 echo "Test #3 fails."
 42 fi
 43
 44
 45 if [ "$a" -eq 98 -o "$b" -eq 47 ]
 46 then
 47 echo "Test #4 succeeds."
 48 else
 49 echo "Test #4 fails."
 50 fi
 51
 52
 53 a=rhino
 54 b=crocodile
 55 if [ "$a" = rhino ] && [ "$b" = crocodile ]
 56 then
 57 echo "Test #5 succeeds."
 58 else
 59 echo "Test #5 fails."
 60 fi
 61
 62 exit 0
 ################################End Script#########################################
 &&和||操作也能在算術(shù)運(yùn)算的上下文中找到.
 bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0))
 1 0 1 0
 
 混雜操作:
 , 逗號(hào)操作符
 逗號(hào)操作符可以連接2個(gè)或多個(gè)算術(shù)運(yùn)算.所有的操作都會(huì)被執(zhí)行,但是只有最后一個(gè)
 操作作為結(jié)果.
 1 let "t1 = ((5 + 3, 7 - 1, 15 - 4))"
 2 echo "t1 = $t1" # t1 = 11
 3
 4 let "t2 = ((a = 9, 15 / 3))" # Set "a" and calculate "t2".
 5 echo "t2 = $t2 a = $a" # t2 = 5 a = 9
 ","主要用在for循環(huán)中,具體見Example 10-12.
 
 
 8.2 數(shù)字常量
 ------------
 shell腳本默認(rèn)都是將數(shù)字作為10進(jìn)制數(shù)處理,除非這個(gè)數(shù)字某種特殊的標(biāo)記法或前綴開頭.
 以0開頭就是8進(jìn)制.以0x開頭就是16進(jìn)制數(shù).使用BASE#NUMBER這種形式可以表示其它進(jìn)制
 表示法
 
 Example 8-4 數(shù)字常量的處理
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # numbers.sh: 數(shù)字常量的幾種不同的表示法
 3
 4 # 10進(jìn)制: 默認(rèn)
 5 let "dec = 32"
 6 echo "decimal number = $dec" # 32
 7 # 一切都很正常
 8
 9
 10 # 8進(jìn)制: 以'0'(零)開頭
 11 let "oct = 032"
 12 echo "octal number = $oct" # 26
 13 # 表達(dá)式的結(jié)果用10進(jìn)制表示.
 14 #
 15
 16 # 16進(jìn)制表示:數(shù)字以'0x'或者'0X'開頭
 17 let "hex = 0x32"
 18 echo "hexadecimal number = $hex" # 50
 19 # 表達(dá)式的結(jié)果用10進(jìn)制表示.
 20
 21 # 其它進(jìn)制: BASE#NUMBER
 22 # BASE between 2 and 64.
 22 # 2到64進(jìn)制都可以.
 23 # NUMBER必須在BASE的范圍內(nèi),具體見下邊.
 24
 25
 26 let "bin = 2#111100111001101"
 27 echo "binary number = $bin" # 31181
 28
 29 let "b32 = 32#77"
 30 echo "base-32 number = $b32" # 231
 31
 32 let "b64 = 64#@_"
 33 echo "base-64 number = $b64" # 4031
 34 # 這種64進(jìn)制的表示法中的每位數(shù)字都必須在64進(jìn)制表示法的限制字符內(nèi).
 35 # 10 個(gè)數(shù)字+ 26 個(gè)小寫字母+ 26 個(gè)大寫字母+ @ + _
 36
 37
 38 echo
 39
 40 echo $((36#zz)) $((2#10101010)) $((16#AF16)) $((53#1aA))
 41 # 1295 170 44822 3375
 42
 43
 44 # 重要的注意事項(xiàng):
 45 # ---------------
 46 # 如果使用的每位數(shù)字超出了這個(gè)進(jìn)制表示法規(guī)定字符的范圍的話,
 47 #+ 將給出一個(gè)錯(cuò)誤消息.
 48
 49 let "bad_oct = 081"
 50 # (部分的) 錯(cuò)誤消息輸出:
 51 # bad_oct = 081: too great for base (error token is "081")
 52 # Octal numbers use only digits in the range 0 - 7.
 53
 54 exit 0 # Thanks, Rich Bartell and Stephane Chazelas, for clarification.
 ################################End Script#########################################
 
 
 
 第三部分 超越基本
 ++++++++++++++++++++
 
 
 第9章 變量重游
 ================
 如果變量使用恰當(dāng),將會(huì)增加腳本的能量和靈活性.但是前提是這需要仔細(xì)學(xué)習(xí)變量的細(xì)節(jié)知識(shí).
 
 
 9.1 內(nèi)部變量
 ------------
 Builtin variable
 這些內(nèi)建的變量,將影響bash腳本的行為.
 
 $BASH
 這個(gè)變量將指向Bash的二進(jìn)制執(zhí)行文件的位置.
 bash$ echo $BASH
 /bin/bash
 
 $BASH_ENV
 這個(gè)環(huán)境變量將指向一個(gè)Bash啟動(dòng)文件,這個(gè)啟動(dòng)文件將在調(diào)用一個(gè)腳本時(shí)被讀取.
 
 $BASH_SUBSHELL
 這個(gè)變量將提醒subshell的層次,這是一個(gè)在version3才被添加到Bash中的新特性.
 見Example 20-1.
 
 $BASH_VERSINFO[n]
 記錄Bash安裝信息的一個(gè)6元素的數(shù)組.與下邊的$BASH_VERSION很像,但這個(gè)更加詳細(xì).
 1 # Bash version info:
 2
 3 for n in 0 1 2 3 4 5
 4 do
 5 echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
 6 done
 7
 8 # BASH_VERSINFO[0] = 3 # 主版本號(hào)
 9 # BASH_VERSINFO[1] = 00 # 次版本號(hào)
 10 # BASH_VERSINFO[2] = 14 # Patch 次數(shù).
 11 # BASH_VERSINFO[3] = 1 # Build version.
 12 # BASH_VERSINFO[4] = release # Release status.
 13 # BASH_VERSINFO[5] = i386-redhat-linux-gnu # Architecture
 
 $BASH_VERSION
 安裝在系統(tǒng)上的Bash的版本號(hào).
 bash$ echo $BASH_VERSION
 3.00.14(1)-release
 tcsh% echo $BASH_VERSION
 BASH_VERSION: Undefined variable.
 使用這個(gè)變量對(duì)于判斷系統(tǒng)上到底運(yùn)行的是那個(gè)shll來說是一種非常好的辦法.$SHELL
 有時(shí)將不能給出正確的答案.
 
 $DIRSTACK
 在目錄棧中最上邊的值(將受到pushd和popd的影響).
 這個(gè)內(nèi)建的變量與dirs命令是保持一致的,但是dirs命令將顯示目錄棧的整個(gè)內(nèi)容.
 
 $EDITOR
 腳本調(diào)用的默認(rèn)編輯器,一般是vi或者是emacs.
 
 $EUID
 "effective"用戶ID號(hào).
 當(dāng)前用戶被假定的任何id號(hào).可能在su命令中使用.
 注意:$EUID并不一定與$UID相同.
 
 $FUNCNAME
 當(dāng)前函數(shù)的名字.
 1 xyz23 ()
 2 {
 3 echo "$FUNCNAME now executing." # xyz23 現(xiàn)在正在被執(zhí)行.
 4 }
 5
 6 xyz23
 7
 8 echo "FUNCNAME = $FUNCNAME" # FUNCNAME =
 9 # 出了函數(shù)就變?yōu)镹ull值了.
 
 $GLOBIGNORE
 一個(gè)文件名的模式匹配列表,如果在file globbing中匹配到的文件包含這個(gè)列表中的
 某個(gè)文件,那么這個(gè)文件將被從匹配到的文件中去掉.
 
 $GROUPS
 當(dāng)前用戶屬于的組.
 這是一個(gè)當(dāng)前用戶的組id列表(數(shù)組),就像在/etc/passwd中記錄的一樣.
 root# echo $GROUPS
 0
 
 root# echo ${GROUPS[1]}
 1
 
 root# echo ${GROUPS[5]}
 6
 
 $HOME
 用戶的home目錄,一般都是/home/username(見Example 9-14)
 
 $HOSTNAME
 hostname命令將在一個(gè)init腳本中,在啟動(dòng)的時(shí)候分配一個(gè)系統(tǒng)名字.
 gethostname()函數(shù)將用來設(shè)置這個(gè)$HOSTNAME內(nèi)部變量.(見Example 9-14)
 
 $HOSTTYPE
 主機(jī)類型
 就像$MACHTYPE,識(shí)別系統(tǒng)的硬件.
 bash$ echo $HOSTTYPE
 i686
 
 $IFS
 內(nèi)部域分隔符.
 這個(gè)變量用來決定Bash在解釋字符串時(shí)如何識(shí)別域,或者單詞邊界.
 $IFS默認(rèn)為空白(空格,tab,和新行),但可以修改,比如在分析逗號(hào)分隔的數(shù)據(jù)文件時(shí).
 注意:$*使用$IFS中的第一個(gè)字符,具體見Example 5-1.
 bash$ echo $IFS | cat -vte
 $
 
 bash$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
 w:x:y:o
 
 注意:$IFS并不像它處理其它字符一樣處理空白.
 
 Example 9-1 $IFS和空白
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # $IFS 處理空白的方法,與處理其它字符不同.
 3
 4 output_args_one_per_line()
 5 {
 6 for arg
 7 do echo "[$arg]"
 8 done
 9 }
 10
 11 echo; echo "IFS=\" \""
 12 echo "-------"
 13
 14 IFS=" "
 15 var=" a b c "
 16 output_args_one_per_line $var # output_args_one_per_line `echo " a b c "`
 17 #
 18 # [a]
 19 #
 20 # [c]
 21
 22
 23 echo; echo "IFS=:"
 24 echo "-----"
 25
 26 IFS=:
 27 var=":a::b:c:::" # 與上邊的一樣,但是用" "替換了":"
 28 output_args_one_per_line $var
 29 #
 30 # []
 31 # [a]
 32 # []
 33 #
 34 # [c]
 35 # []
 36 # []
 37 # []
 38
 39 # 同樣的事情也會(huì)發(fā)生在awk中的"FS"域分隔符.
 40
 41 # Thank you, Stephane Chazelas.
 42
 43 echo
 44
 45 exit 0
 ################################End Script#########################################
 Example 12-37也是使用$IFS的另一個(gè)啟發(fā)性的例子.
 
 $IGNOREEOF
 忽略EOF: 告訴shell在log out之前要忽略多少文件結(jié)束符(control-D).
 
 $LC_COLLATE
 常在.bashrc或/etc/profile中設(shè)置,這個(gè)變量用來在文件名擴(kuò)展和模式匹配校對(duì)順序.
 如果$LC_COLLATE被錯(cuò)誤的設(shè)置,那么將會(huì)在filename globbing中引起錯(cuò)誤的結(jié)果.
 
 注意:在2.05以后的Bash版本中,filename globbing將不在對(duì)[]中的字符區(qū)分大小寫.
 比如:ls [A-M]* 將即匹配File1.txt也會(huì)匹配file1.txt.為了恢復(fù)[]的習(xí)慣用法,
 設(shè)置$LC_COLLATE的值為c,使用export LC_COLLATE=c 在/etc/profile或者是
 ~/.bashrc中.
 
 $LC_CTYPE
 這個(gè)內(nèi)部變量用來控制globbing和模式匹配的字符串解釋.
 
 $LINENO
 這個(gè)變量記錄它所在的shell腳本中它所在行的行號(hào).這個(gè)變量一般用于調(diào)試目的.
 1 # *** BEGIN DEBUG BLOCK ***
 2 last_cmd_arg=$_ # Save it.
 3
 4 echo "At line number $LINENO, variable \"v1\" = $v1"
 5 echo "Last command argument processed = $last_cmd_arg"
 6 # *** END DEBUG BLOCK ***
 
 $MACHTYPE
 系統(tǒng)類型
 提示系統(tǒng)硬件
 bash$ echo $MACHTYPE
 i686
 
 $OLDPWD
 老的工作目錄("OLD-print-working-directory",你所在的之前的目錄)
 
 $OSTYPE
 操作系統(tǒng)類型.
 bash$ echo $OSTYPE
 linux
 
 $PATH
 指向Bash外部命令所在的位置,一般為/usr/bin,/usr/X11R6/bin,/usr/local/bin等.
 當(dāng)給出一個(gè)命令時(shí),Bash將自動(dòng)對(duì)$PATH中的目錄做一張hash表.$PATH中以":"分隔的
 目錄列表將被存儲(chǔ)在環(huán)境變量中.一般的,系統(tǒng)存儲(chǔ)的$PATH定義在/ect/processed或
 ~/.bashrc中(見Appendix G).
 
 bash$ echo $PATH
 /bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin
 
 PATH=${PATH}:/opt/bin將把/opt/bin目錄附加到$PATH變量中.在腳本中,這是一個(gè)
 添加目錄到$PATH中的便捷方法.這樣在這個(gè)腳本退出的時(shí)候,$PATH將會(huì)恢復(fù)(因?yàn)檫@個(gè)
 shell是個(gè)子進(jìn)程,像這樣的一個(gè)腳本是不會(huì)將它的父進(jìn)程的環(huán)境變量修改的)
 
 注意:當(dāng)前的工作目錄"./"一般都在$PATH中被省去.
 
 $PIPESTATUS
 數(shù)組變量將保存最后一個(gè)運(yùn)行的前臺(tái)管道的退出碼.有趣的是,這個(gè)退出碼和最后一個(gè)命令
 運(yùn)行的退出碼并不一定相同.
 bash$ echo $PIPESTATUS
 0
 
 bash$ ls -al | bogus_command
 bash: bogus_command: command not found
 bash$ echo $PIPESTATUS
 141
 
 bash$ ls -al | bogus_command
 bash: bogus_command: command not found
 bash$ echo $?
 127
 
 $PIPESTATUS數(shù)組的每個(gè)成員都會(huì)保存一個(gè)管道命令的退出碼,$PIPESTATUS[0]保存第
 一個(gè)管道命令的退出碼,$PIPESTATUS[1]保存第2個(gè),以此類推.
 
 注意:$PIPESTATUS變量在一個(gè)login shell中可能會(huì)包含一個(gè)錯(cuò)誤的0值(3.0以下版本)
 tcsh% bash
 
 bash$ who | grep nobody | sort
 bash$ echo ${PIPESTATUS
- }
 0
 包含在腳本中的上邊這行將會(huì)產(chǎn)生一個(gè)期望的輸出0 1 0.
 
 注意:在某些上下文$PIPESTATUS可能不會(huì)給出正確的結(jié)果.
 bash$ echo $BASH_VERSION
 3.00.14(1)-release
 
 bash$ $ ls | bogus_command | wc
 bash: bogus_command: command not found
 0 0 0
 
 bash$ echo ${PIPESTATUS[@]}
 141 127 0
 
 Chet Ramey把上邊輸出不成確原因歸咎于ls的行為.因?yàn)槿绻裭s的結(jié)果放到管道上,
 并且這個(gè)輸出沒被讀取,那么SIGPIPE將會(huì)kill掉它,并且退出碼變?yōu)?41,而不是我們期
 望的0.這種情況也會(huì)發(fā)生在tr命令中.
 
 注意:$PIPESTATUS是一個(gè)"volatile"變量.在任何命令插入之前,并且在pipe詢問之后,
 這個(gè)變量需要立即被捕捉.
 bash$ $ ls | bogus_command | wc
 bash: bogus_command: command not found
 0 0 0
 
 bash$ echo ${PIPESTATUS[@]}
 0 127 0
 
 bash$ echo ${PIPESTATUS[@]}
 0
 
 $PPID
 一個(gè)進(jìn)程的$PPID就是它的父進(jìn)程的進(jìn)程id(pid).[1]
 使用pidof命令對(duì)比一下.
 
 $PROMPT_COMMAND
 這個(gè)變量保存一個(gè)在主提示符($PS1)顯示之前需要執(zhí)行的命令.
 
 $PS1
 主提示符,具體見命令行上的顯示.
 
 $PS2
 第2提示符,當(dāng)你需要額外的輸入的時(shí)候?qū)?huì)顯示,默認(rèn)為">".
 
 $PS3
 第3提示符,在一個(gè)select循環(huán)中顯示(見Example 10-29).
 
 $PS4
 第4提示符,當(dāng)使用-x選項(xiàng)調(diào)用腳本時(shí),這個(gè)提示符將出現(xiàn)在每行的輸出前邊.
 默認(rèn)為"+".
 
 $PWD
 工作目錄(你當(dāng)前所在的目錄).
 與pwd內(nèi)建命令作用相同.
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 E_WRONG_DIRECTORY=73
 4
 5 clear # 清屏.
 6
 7 TargetDirectory=/home/bozo/projects/GreatAmericanNovel
 8
 9 cd $TargetDirectory
 10 echo "Deleting stale files in $TargetDirectory."
 11
 12 if [ "$PWD" != "$TargetDirectory" ]
 13 then # 防止偶然刪除錯(cuò)誤的目錄
 14 echo "Wrong directory!"
 15 echo "In $PWD, rather than $TargetDirectory!"
 16 echo "Bailing out!"
 17 exit $E_WRONG_DIRECTORY
 18 fi
 19
 20 rm -rf *
 21 rm .[A-Za-z0-9]* # Delete dotfiles.
 21 rm .[A-Za-z0-9]* # 刪除"."文件(隱含文件).
 22 # rm -f .[^.]* ..?* 為了刪除以多個(gè)"."開頭的文件.
 23 # (shopt -s dotglob; rm -f *) 也行.
 24 # Thanks, S.C. for pointing this out.
 25
 26 # 文件名能夠包含0-255范圍的所有字符,除了"/".
 27 # 刪除以各種詭異字符開頭的文件將作為一個(gè)練習(xí)留給大家.
 28
 29 # 這里預(yù)留給其他的必要操作.
 30
 31 echo
 32 echo "Done."
 33 echo "Old files deleted in $TargetDirectory."
 34 echo
 35
 36
 37 exit 0
 ################################End Script#########################################
 
 $REPLY
 read命令如果沒有給變量,那么輸入將保存在$REPLY中.在select菜單中也可用,但是只
 提供選擇的變量的項(xiàng)數(shù),而不是變量本身的值.
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # reply.sh
 3
 4 # REPLY是'read'命令結(jié)果保存的默認(rèn)變量.
 5
 6 echo
 7 echo -n "What is your favorite vegetable? "
 8 read
 9
 10 echo "Your favorite vegetable is $REPLY."
 11 # 當(dāng)且僅當(dāng)在沒有變量提供給"read"命令時(shí),
 12 #+ REPLY才保存最后一個(gè)"read"命令讀入的值.
 13
 14 echo
 15 echo -n "What is your favorite fruit? "
 16 read fruit
 17 echo "Your favorite fruit is $fruit."
 18 echo "but..."
 19 echo "Value of \$REPLY is still $REPLY."
 20 # $REPLY還是保存著上一個(gè)read命令的值,
 21 #+ 因?yàn)樽兞?fruit被傳入到了這個(gè)新的"read"命令中.
 22
 23 echo
 24
 25 exit 0
 ################################End Script#########################################
 
 $SECONDS
 這個(gè)腳本已經(jīng)運(yùn)行的時(shí)間(單位為秒).
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 TIME_LIMIT=10
 4 INTERVAL=1
 5
 6 echo
 7 echo "Hit Control-C to exit before $TIME_LIMIT seconds."
 8 echo
 9
 10 while [ "$SECONDS" -le "$TIME_LIMIT" ]
 11 do
 12 if [ "$SECONDS" -eq 1 ]
 13 then
 14 units=second
 15 else
 16 units=seconds
 17 fi
 18
 19 echo "This script has been running $SECONDS $units."
 20 # 在一臺(tái)比較慢的或者是負(fù)載很大的機(jī)器上,這個(gè)腳本可能會(huì)跳過幾次循環(huán)
 21 #+ 在一個(gè)while循環(huán)中.
 22 sleep $INTERVAL
 23 done
 24
 25 echo -e "\a" # Beep!
 26
 27 exit 0
 ################################End Script#########################################
 
 $SHELLOPTS
 這個(gè)變量里保存shell允許的選項(xiàng),這個(gè)變量是只讀的.
 bash$ echo $SHELLOPTS
 braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs
 
 $SHLVL
 Shell層次,就是shell層疊的層次,如果是命令行那$SHLVL就是1,如果命令行執(zhí)行的腳
 本中,$SHLVL就是2,以此類推.
 
 $TMOUT
 如果$TMOUT環(huán)境變量被設(shè)置為一個(gè)非零的時(shí)間值,那么在過了這個(gè)指定的時(shí)間之后,
 shell提示符將會(huì)超時(shí),這會(huì)引起一個(gè)logout.
 
 在2.05b版本的Bash中,已經(jīng)支持在一個(gè)帶有read命令的腳本中使用$TMOUT變量.
 1 # 需要使用Bash v2.05b或者以后的版本上
 2
 3 TMOUT=3 # Prompt times out at three seconds.
 3 TMOUT=3 # 設(shè)置超時(shí)的時(shí)間為3秒
 4
 5 echo "What is your favorite song?"
 6 echo "Quickly now, you only have $TMOUT seconds to answer!"
 7 read song
 8
 9 if [ -z "$song" ]
 10 then
 11 song="(no answer)"
 12 # 默認(rèn)響應(yīng).
 13 fi
 14
 15 echo "Your favorite song is $song."
 
 這里有一個(gè)更復(fù)雜的方法來在一個(gè)腳本中實(shí)現(xiàn)超時(shí)功能.一種辦法就是建立一個(gè)時(shí)間循
 環(huán),在超時(shí)的時(shí)候通知腳本.不過,這也需要一個(gè)信號(hào)處理機(jī)制,在超時(shí)的時(shí)候來產(chǎn)生中
 斷.
 (參見Example 29-5)
 
 Example 9-2 時(shí)間輸入
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # timed-input.sh
 3
 4 # TMOUT=3 在新版本的Bash上也能工作.
 5
 6
 7 TIMELIMIT=3 # 在這個(gè)例子上是3秒,也可以設(shè)其他的值.
 8
 9 PrintAnswer()
 10 {
 11 if [ "$answer" = TIMEOUT ]
 12 then
 13 echo $answer
 14 else # 別想混合著兩個(gè)例子.
 15 echo "Your favorite veggie is $answer"
 16 kill $! # kill將不再需要TimerOn函數(shù)運(yùn)行在后臺(tái).
 17 # $! 是運(yùn)行在后臺(tái)的最后一個(gè)工作的PID.
 18 fi
 19
 20 }
 21
 22
 23
 24 TimerOn()
 25 {
 26 sleep $TIMELIMIT && kill -s 14 $$ &
 27 # 等待3秒,然后發(fā)送一個(gè)信號(hào)給腳本.
 28 }
 29
 30 Int14Vector()
 31 {
 32 answer="TIMEOUT"
 33 PrintAnswer
 34 exit 14
 35 }
 36
 37 trap Int14Vector 14 # 為了我們的目的,時(shí)間中斷(14)被破壞了.
 38
 39 echo "What is your favorite vegetable "
 40 TimerOn
 41 read answer
 42 PrintAnswer
 43
 44
 45 # 很明顯的,這是一個(gè)拼湊的實(shí)現(xiàn).
 46 #+ 然而使用"-t"選項(xiàng)來"read"的話,將會(huì)簡化這個(gè)任務(wù).
 47 # 見"t-out.sh",在下邊.
 48
 49 # 如果你需要一個(gè)真正的幽雅的寫法...
 50 #+ 建議你使用c/c++來寫這個(gè)應(yīng)用,
 51 #+ 使用合適的庫來完成這個(gè)任務(wù),比如'alarm'和'setitimer'.
 52
 53 exit 0
 ################################End Script#########################################
 使用stty也是一種選擇.
 
 Example 9-3 再來一個(gè)時(shí)間輸入
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # timeout.sh
 3
 4 # Stephane Chazelas編寫,
 5 #+ 本書作者進(jìn)行了一些修改.
 6
 7 INTERVAL=5 # timeout間隔
 8
 9 timedout_read() {
 10 timeout=$1
 11 varname=$2
 12 old_tty_settings=`stty -g`
 13 stty -icanon min 0 time ${timeout}0
 14 eval read $varname # 或者就是 read $varname
 15 stty "$old_tty_settings"
 16 # 察看"stty"的man頁.
 17 }
 18
 19 echo; echo -n "What's your name? Quick! "
 20 timedout_read $INTERVAL your_name
 21
 22 # 這種方法可能不是每個(gè)終端類型都可以正常使用的.
 23 # 最大的timeout依賴于具體的終端.
 24 #+ (一般都是25.5秒).
 25
 26 echo
 27
 28 if [ ! -z "$your_name" ] # If name input before timeout...
 29 then
 30 echo "Your name is $your_name."
 31 else
 32 echo "Timed out."
 33 fi
 34
 35 echo
 36
 37 # 這個(gè)腳本的行為可能與"timed-input.sh"有點(diǎn)不同.
 38 # 在每次按鍵的時(shí)候,計(jì)數(shù)器都會(huì)重置.
 39
 40 exit 0
 ################################End Script#########################################
 或許,最簡單的辦法就是使用-t選項(xiàng)來read了.
 
 Example 9-4 Timed read
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # t-out.sh
 3 # "syngin seven"的一個(gè)很好的提議 (thanks).
 4
 5
 6 TIMELIMIT=4 # 4 seconds
 7
 8 read -t $TIMELIMIT variable <&1
 9 # ^^^
 10 # 在這個(gè)例子中,對(duì)于Bash 1.x和2.x就需要使用"<&1"
 11 # 但對(duì)于Bash 3.x就不需要.
 12
 13 echo
 14
 15 if [ -z "$variable" ] # Is null?
 16 then
 17 echo "Timed out, variable still unset."
 18 else
 19 echo "variable = $variable"
 20 fi
 21
 22 exit 0
 ################################End Script#########################################
 
 $UID
 用戶ID號(hào).
 當(dāng)前用戶的id號(hào),在/etc/passwd中記錄.
 這個(gè)值不會(huì)因?yàn)橛脩羰褂昧藄u命令而改變.$UID是只讀變量,不容易在命令行或者是腳
 本中被修改,并且和內(nèi)建的id命令很相像.
 Example 9-5 我是root?
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # am-i-root.sh: 我是不是root用戶?
 3
 4 ROOT_UID=0 # Root的$UID是0.
 5
 6 if [ "$UID" -eq "$ROOT_UID" ] # 是否是root用戶,請(qǐng)站出來.
 7 then
 8 echo "You are root."
 9 else
 10 echo "You are just an ordinary user (but mom loves you just the same)."
 11 fi
 12
 13 exit 0
 14
 15
 16 # ============================================================= #
 17 # 下邊的代碼將不被執(zhí)行,因?yàn)槟_本已經(jīng)退出了.
 18
 19 # 檢驗(yàn)是root用戶的一種可選方法:
 20
 21 ROOTUSER_NAME=root
 22
 23 username=`id -nu` # Or... username=`whoami`
 24 if [ "$username" = "$ROOTUSER_NAME" ]
 25 then
 26 echo "Rooty, toot, toot. You are root."
 27 else
 28 echo "You are just a regular fella."
 29 fi
 ################################End Script#########################################
 見例子Example 2-3
 注意:變量$ENV,$LOGNAME,$MAIL,$TERM,$USER,和$USERNAME并不是Bash的內(nèi)建變量.它
 們經(jīng)常被設(shè)置成環(huán)境變量,它們一般都放在Bash的安裝文件中.$SHELL,用戶登錄的
 shell的名字,可能是從/etc/passwd設(shè)置的,也可能是在一個(gè)"init"腳本中設(shè)置的,同樣
 的,它也不是Bash的內(nèi)建變量.
 tcsh% echo $LOGNAME
 bozo
 tcsh% echo $SHELL
 /bin/tcsh
 tcsh% echo $TERM
 rxvt
 
 bash$ echo $LOGNAME
 bozo
 bash$ echo $SHELL
 /bin/tcsh
 bash$ echo $TERM
 rxvt
 
 位置參數(shù)
 $0, $1, $2,等等...
 位置參數(shù),從命令行傳遞給腳本,或者是傳遞給函數(shù).或者賦職給一個(gè)變量.
 (具體見Example 4-5和Example 11-15)
 
 $#
 命令行或者是位置參數(shù)的個(gè)數(shù).(見Example 33-2)
 
 $*
 所有的位置參數(shù),被作為一個(gè)單詞.
 注意:"$*"必須被""引用.
 
 $@
 與$*同義,但是每個(gè)參數(shù)都是一個(gè)獨(dú)立的""引用字串,這就意味著參數(shù)被完整地傳遞,
 并沒有被解釋和擴(kuò)展.這也意味著,每個(gè)參數(shù)列表中的每個(gè)參數(shù)都被當(dāng)成一個(gè)獨(dú)立的
 單詞.
 注意:"$@"必須被引用.
 
 Example 9-6 arglist:通過$*和$@列出所有的參數(shù)
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # arglist.sh
 3 # 多使用幾個(gè)參數(shù)來調(diào)用這個(gè)腳本,比如"one tow three".
 4
 5 E_BADARGS=65
 6
 7 if [ ! -n "$1" ]
 8 then
 9 echo "Usage: `basename $0` argument1 argument2 etc."
 10 exit $E_BADARGS
 11 fi
 12
 13 echo
 14
 15 index=1 # 初始化數(shù)量.
 16
 17 echo "Listing args with \"\$*\":"
 18 for arg in "$*" # 如果"$*"不被""引用,那么將不能正常地工作
 19 do
 20 echo "Arg #$index = $arg"
 21 let "index+=1"
 22 done # $* sees all arguments as single word.
 22 done # $* 認(rèn)為所有的參數(shù)為一個(gè)單詞
 23 echo "Entire arg list seen as single word."
 24
 25 echo
 26
 27 index=1 # 重置數(shù)量.
 28 # 如果你忘了這句會(huì)發(fā)生什么?
 29
 30 echo "Listing args with \"\$@\":"
 31 for arg in "$@"
 32 do
 33 echo "Arg #$index = $arg"
 34 let "index+=1"
 35 done # $@ 認(rèn)為每個(gè)參數(shù)都一個(gè)單獨(dú)的單詞.
 36 echo "Arg list seen as separate words."
 37
 38 echo
 39
 40 index=1 # 重置數(shù)量.
 41
 42 echo "Listing args with \$* (unquoted):"
 43 for arg in $*
 44 do
 45 echo "Arg #$index = $arg"
 46 let "index+=1"
 47 done # 未""引用的$*把參數(shù)作為獨(dú)立的單詞.
 48 echo "Arg list seen as separate words."
 49
 50 exit 0
 ################################End Script#########################################
 
 在shift命令后邊,$@將保存命令行中剩余的參數(shù),而$1被丟掉了.
 1 #!/bin/bash
 2 # 使用 ./scriptname 1 2 3 4 5 來調(diào)用這個(gè)腳本
 3
 4 echo "$@" # 1 2 3 4 5
 5 shift
 6 echo "$@" # 2 3 4 5
 7 shift
 8 echo "$@" # 3 4 5
 9
 10 # 每個(gè)"shift"都丟棄$1.
 11 # "$@" 將包含剩下的參數(shù).
 $@也作為為工具使用,用來過濾傳給腳本的輸入.
 cat "$@"結(jié)構(gòu)接受從stdin傳來的輸入,也接受從參數(shù)中指定的文件傳來的輸入.
 具體見Example 12-21和Example 12-22.
 
 注意:$*和$@的參數(shù)有時(shí)會(huì)不一致,發(fā)生令人迷惑的行為,這依賴于$IFS的設(shè)置.
 
 Example 9-7 不一致的$*和$@行為
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 # "$*"和"$@"的古怪行為,
 4 #+ 依賴于它們是否被""引用.
 5 # 單詞拆分和換行的不一致處理.
 6
 7
 8 set -- "First one" "second" "third:one" "" "Fifth: :one"
 9 # 設(shè)置這個(gè)腳本參數(shù),$1,$2,等等.
 10
 11 echo
 12
 13 echo 'IFS unchanged, using "$*"'
 14 c=0
 15 for i in "$*" # 引用
 16 do echo "$((c+=1)): [$i]" # 這行在下邊的每個(gè)例子中都一樣.
 17 # Echo參數(shù).
 18 done
 19 echo ---
 20
 21 echo 'IFS unchanged, using $*'
 22 c=0
 23 for i in $* # 未引用
 24 do echo "$((c+=1)): [$i]"
 25 done
 26 echo ---
 27
 28 echo 'IFS unchanged, using "$@"'
 29 c=0
 30 for i in "$@"
 31 do echo "$((c+=1)): [$i]"
 32 done
 33 echo ---
 34
 35 echo 'IFS unchanged, using $@'
 36 c=0
 37 for i in $@
 38 do echo "$((c+=1)): [$i]"
 39 done
 40 echo ---
 41
 42 IFS=:
 43 echo 'IFS=":", using "$*"'
 44 c=0
 45 for i in "$*"
 46 do echo "$((c+=1)): [$i]"
 47 done
 48 echo ---
 49
 50 echo 'IFS=":", using $*'
 51 c=0
 52 for i in $*
 53 do echo "$((c+=1)): [$i]"
 54 done
 55 echo ---
 56
 57 var=$*
 58 echo 'IFS=":", using "$var" (var=$*)'
 59 c=0
 60 for i in "$var"
 61 do echo "$((c+=1)): [$i]"
 62 done
 63 echo ---
 64
 65 echo 'IFS=":", using $var (var=$*)'
 66 c=0
 67 for i in $var
 68 do echo "$((c+=1)): [$i]"
 69 done
 70 echo ---
 71
 72 var="$*"
 73 echo 'IFS=":", using $var (var="$*")'
 74 c=0
 75 for i in $var
 76 do echo "$((c+=1)): [$i]"
 77 done
 78 echo ---
 79
 80 echo 'IFS=":", using "$var" (var="$*")'
 81 c=0
 82 for i in "$var"
 83 do echo "$((c+=1)): [$i]"
 84 done
 85 echo ---
 86
 87 echo 'IFS=":", using "$@"'
 88 c=0
 89 for i in "$@"
 90 do echo "$((c+=1)): [$i]"
 91 done
 92 echo ---
 93
 94 echo 'IFS=":", using $@'
 95 c=0
 96 for i in $@
 97 do echo "$((c+=1)): [$i]"
 98 done
 99 echo ---
 100
 101 var=$@
 102 echo 'IFS=":", using $var (var=$@)'
 103 c=0
 104 for i in $var
 105 do echo "$((c+=1)): [$i]"
 106 done
 107 echo ---
 108
 109 echo 'IFS=":", using "$var" (var=$@)'
 110 c=0
 111 for i in "$var"
 112 do echo "$((c+=1)): [$i]"
 113 done
 114 echo ---
 115
 116 var="$@"
 117 echo 'IFS=":", using "$var" (var="$@")'
 118 c=0
 119 for i in "$var"
 120 do echo "$((c+=1)): [$i]"
 121 done
 122 echo ---
 123
 124 echo 'IFS=":", using $var (var="$@")'
 125 c=0
 126 for i in $var
 127 do echo "$((c+=1)): [$i]"
 128 done
 129
 130 echo
 131
 132 # 用ksh或者zsh -y來試試這個(gè)腳本.
 133
 134 exit 0
 135
 136 # This example script by Stephane Chazelas,
 137 # and slightly modified by the document author.
 ################################End Script#########################################
 注意:$@和$*中的參數(shù)只有在""中才會(huì)不同.
 
 Example 9-8 當(dāng)$IFS為空時(shí)的$*和$@
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 # 如果$IFS被設(shè)置為空時(shí),
 4 #+ 那么"$*" 和"$@" 將不會(huì)象期望那樣echo出位置參數(shù).
 5
 6 mecho () # Echo 位置參數(shù).
 7 {
 8 echo "$1,$2,$3";
 9 }
 10
 11
 12 IFS="" # 設(shè)置為空.
 13 set a b c # 位置參數(shù).
 14
 15 mecho "$*" # abc,,
 16 mecho $* # a,b,c
 17
 18 mecho $@ # a,b,c
 19 mecho "$@" # a,b,c
 20
 21 # 當(dāng)$IFS設(shè)置為空時(shí),$* 和$@ 的行為依賴于
 22 #+ 正在運(yùn)行的Bash或者sh的版本.
 23 # 所以在腳本中使用這種"feature"不是明智的行為.
 24
 25
 26 # Thanks, Stephane Chazelas.
 27
 28 exit 0
 ################################End Script#########################################
 
 其他的特殊參數(shù)
 
 $-
 傳遞給腳本的falg(使用set命令).參考Example 11-15.
 
 注意:這起初是ksh的特征,后來被引進(jìn)到Bash中,但不幸的是,在Bash中它看上去也不
 能可靠的工作.使用它的一個(gè)可能的方法就是讓這個(gè)腳本進(jìn)行自我測(cè)試(查看是否是交
 互的).
 
 $!
 在后臺(tái)運(yùn)行的最后的工作的PID(進(jìn)程ID).
 1 LOG=$0.log
 2
 3 COMMAND1="sleep 100"
 4
 5 echo "Logging PIDs background commands for script: $0" >> "$LOG"
 6 # 所以它們可以被監(jiān)控,并且在必要的時(shí)候kill掉.
 7 echo >> "$LOG"
 8
 9 # Logging 命令.
 10
 11 echo -n "PID of \"$COMMAND1\": " >> "$LOG"
 12 ${COMMAND1} &
 13 echo $! >> "$LOG"
 14 # PID of "sleep 100": 1506
 15
 16 # Thank you, Jacques Lederer, for suggesting this.
 
 
 1 possibly_hanging_job & { sleep ${TIMEOUT}; eval 'kill -9 $!' &> /dev/null; }
 2 # 強(qiáng)制結(jié)束一個(gè)品行不良的程序.
 3 # 很有用,比如在init腳本中.
 4
 5 # Thank you,Sylvain Fourmanoit,for this creative use of the "!" variable.
 
 $_
 保存之前執(zhí)行的命令的最后一個(gè)參數(shù).
 
 Example 9-9 下劃線變量
 ################################Start Script#######################################
 1 #!/bin/bash
 2
 3 echo $_ # /bin/bash
 4 # 只是調(diào)用/bin/bash來運(yùn)行這個(gè)腳本.
 5
 6 du >/dev/null # 將沒有命令的輸出
 7 echo $_ # du
 8
 9 ls -al >/dev/null # 沒有命令輸出
 10 echo $_ # -al (最后的參數(shù))
 11
 12 :
 13 echo $_ # :
 ################################End Script#########################################
 
 $?
 命令,函數(shù)或者腳本本身的退出狀態(tài)(見Example 23-7)
 
 $$
 腳本自身的進(jìn)程ID.這個(gè)變量經(jīng)常用來構(gòu)造一個(gè)"unique"的臨時(shí)文件名.
 (參考Example A-13,Example 29-6,Example 12-28和Example 11-25).
 這通常比調(diào)用mktemp來得簡單.
 
 注意事項(xiàng):
 [1] 當(dāng)前運(yùn)行的腳本的PID為$$.
 [2] "argument"和"parameter"這兩個(gè)單詞經(jīng)常不加區(qū)分的使用.在這整本書中,這兩個(gè)
 單詞的意思完全相同.(在翻譯的時(shí)候就未加區(qū)分,統(tǒng)統(tǒng)翻譯成參數(shù))
 
 
 9.2 操作字符串
 --------------
 Bash支持超多的字符串操作,操作的種類和數(shù)量令人驚異.但不幸的是,這些工具缺乏集中性.
 一些是參數(shù)替換的子集,但是另一些則屬于UNIX的expr命令.這就導(dǎo)致了命令語法的不一致和
 功能的重疊,當(dāng)然也會(huì)引起混亂.
 
 字符串長度
 
 ${#string}
 expr length $string
 expr "$string" : '.*'
 
 1 stringZ=abcABC123ABCabc
 2
 3 echo ${#stringZ} # 15
 4 echo `expr length $stringZ` # 15
 5 echo `expr "$stringZ" : '.*'` # 15
 
 Example 9-10 在一個(gè)文本文件的段間插入空行
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # paragraph-space.sh
 3
 4 # 在一個(gè)不空行的文本文件的段間插入空行.
 5 # Usage: $0 <FILENAME
 6
 7 MINLEN=45 # 可能需要修改這個(gè)值.
 8 # 假定行的長度小于$MINLEN指定的長度
 9 #+ $MINLEN中的值用來描述多少個(gè)字符結(jié)束一個(gè)段.
 10
 11 while read line # 對(duì)于需要多行輸入的文件基本都是這個(gè)樣子
 12 do
 13 echo "$line" # 輸出line.
 14
 15 len=${#line}
 16 if [ "$len" -lt "$MINLEN" ]
 17 then echo # 在短行后邊添加一個(gè)空行
 18 fi
 19 done
 20
 21 exit 0
 ################################End Script#########################################
 
 從字符串開始的位置匹配子串的長度
 
 expr match "$string" '$substring'
 $substring是一個(gè)正則表達(dá)式
 
 expr "$string" : '$substring'
 $substring是一個(gè)正則表達(dá)式
 
 1 stringZ=abcABC123ABCabc
 2 # |------|
 3
 4 echo `expr match "$stringZ" 'abc[A-Z]*.2'` # 8
 5 echo `expr "$stringZ" : 'abc[A-Z]*.2'` # 8
 
 索引
 
 expr index $string $substring
 匹配到子串的第一個(gè)字符的位置.
 
 1 stringZ=abcABC123ABCabc
 2 echo `expr index "$stringZ" C12` # 6
 3 # C position.
 4
 5 echo `expr index "$stringZ" 1c` # 3
 6 # 'c' (in #3 position) matches before '1'.
 
 在C語言中最近的等價(jià)函數(shù)為strchr().
 
 提取子串
 
 ${string:position}
 在string中從位置$position開始提取子串.
 如果$string為"*"或"@",那么將提取從位置$position開始的位置參數(shù),[1]
 
 ${string:position:length}
 在string中從位置$position開始提取$length長度的子串.
 
 ################################Start Script#######################################
 1 stringZ=abcABC123ABCabc
 2 # 0123456789.....
 3 # 0-based indexing.
 4
 5 echo ${stringZ:0} # abcABC123ABCabc
 6 echo ${stringZ:1} # bcABC123ABCabc
 7 echo ${stringZ:7} # 23ABCabc
 8
 9 echo ${stringZ:7:3} # 23A
 10 # 3個(gè)字符長度的子串.
 11
 12
 13
 14 # 有沒有可能從字符結(jié)尾開始,反向提取子串?
 15
 16 echo ${stringZ:-4} # abcABC123ABCabc
 17 # 以${parameter:-default}方式,默認(rèn)是提取完整地字符串.
 18 # 然而 . . .
 19
 20 echo ${stringZ:(-4)} # Cabc
 21 echo ${stringZ: -4} # Cabc
 22 # 現(xiàn)在,它可以工作了.
 23 # 使用圓括號(hào)或者添加一個(gè)空格來轉(zhuǎn)義這個(gè)位置參數(shù).
 24
 25 # Thank you, Dan Jacobson, for pointing this out.
 ################################End Script#########################################
 如果$string參數(shù)為"*"或"@",那將最大的提取從$position開始的$length個(gè)位置參數(shù).
 1 echo ${*:2} # Echo出第2個(gè)和后邊所有的位置參數(shù).
 2 echo ${@:2} # 與前邊相同.
 3
 4 echo ${*:2:3} # 從第2個(gè)開始,Echo出后邊3個(gè)位置參數(shù).
 
 
 expr substr $string $position $length
 在string中從位置$position開始提取$length長度的子串.
 1 stringZ=abcABC123ABCabc
 2 # 123456789......
 3 # 1-based indexing.
 4
 5 echo `expr substr $stringZ 1 2` # ab
 6 echo `expr substr $stringZ 4 3` # ABC
 
 expr match "$string" '\($substring\)'
 從$string的開始位置提取$substring,$substring是一個(gè)正則表達(dá)式.
 
 expr "$string" : '\($substring\)'
 從$string的開始位置提取$substring,$substring是一個(gè)正則表達(dá)式.
 1 stringZ=abcABC123ABCabc
 2 # =======
 3
 4 echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1
 5 echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1
 6 echo `expr "$stringZ" : '\(.......\)'` # abcABC1
 7 # All of the above forms give an identical result.
 
 子串削除
 
 ${string#substring}
 從$string的左邊截掉第一個(gè)匹配的$substring
 ${string##substring}
 從$string的左邊截掉最后一個(gè)個(gè)匹配的$substring
 
 1 stringZ=abcABC123ABCabc
 2 # |----|
 3 # |----------|
 4
 5 echo ${stringZ#a*C} # 123ABCabc
 6 # 截掉'a'和'C'之間最近的匹配.
 7
 8 echo ${stringZ##a*C} # abc
 9 # 截掉'a'和'C'之間最遠(yuǎn)的匹配.
 
 
 ${string%substring}
 從$string的右邊截掉第一個(gè)匹配的$substring
 ${string%%substring}
 從$string的右邊截掉最后一個(gè)匹配的$substring
 
 1 stringZ=abcABC123ABCabc
 2 # ||
 3 # |------------|
 4
 5 echo ${stringZ%b*c} # abcABC123ABCa
 6 # 從$stringZ的后邊開始截掉'b'和'c'之間的最近的匹配
 7
 8 echo ${stringZ%%b*c} # a
 9 # 從$stringZ的后邊開始截掉'b'和'c'之間的最遠(yuǎn)的匹配
 
 Example 9-11 利用修改文件名,來轉(zhuǎn)換圖片格式
 ################################Start Script#######################################
 1 #!/bin/bash
 2 # cvt.sh:
 3 # 把一個(gè)目錄下的所有MacPaint格式的圖片文件都轉(zhuǎn)換為"pbm"格式的圖片文件.
 4
 5 # 使用來自"netpbm"包的"macptopbm"程序,
 6 #+ 這個(gè)程序主要是由Brian Henderson(bryanh@giraffe-data.com)來維護(hù)的.
 7 # Netpbm是大多數(shù)Linux發(fā)行版的標(biāo)準(zhǔn)部分.
 8
 9 OPERATION=macptopbm
 10 SUFFIX=pbm # 新的文件名后綴
 11
 12 if [ -n "$1" ]
 13 then
 14 directory=$1 # 如果目錄名作為第1個(gè)參數(shù)給出...
 15 else
 16 directory=
 
 
總結(jié)
以上是生活随笔為你收集整理的高级Bash脚本编程指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Python实现 ---【简易】1230
- 下一篇: Eclipse 优化
