bash--shell高级编程-特殊字符
shell高級編程-特殊字符
用在腳本和其他地方的特殊字符
注釋. 行首以#(#!是個例外)開頭是注釋.
# This line is a comment.注釋也可以放在于本行命令的后邊.
echo "A comment will follow." # 注釋在這里. # 注意#前邊的空白注釋也可以放在本行行首空白的后面.
# A tab precedes this comment.命令是不能放在同一行上注釋的后邊的. 因為沒有辦法把注釋結束掉, 好讓
同一行上后邊的"代碼生效". 只能夠另起一行來使用下一個命令.
當然, 在echo中轉義的#是不能作為注釋的. 同樣的, #也可以出現在特定的
參數替換結構中, 或者是出現在數字常量表達式中.
標準的引用和轉義字符(" ’ )可以用來轉義#.
某些特定的模式匹配操作也可以使用#.
;
命令分隔符[分號, 即;]. 可以在同一行上寫兩個或兩個以上的命令.
注意一下";"某些情況下需要轉義.
;;
終止case選項[雙分號, 即;;].
"點"命令[句點, 即.]. 等價于source命令(參見 例子 11-21). 這是一個bash的內建命令.
.
"點"作為文件名的一部分. 如果點放在文件名的開頭的話, 那么這個文件將會成為"隱藏"文件,
并且ls命令將不會正常的顯示出這個文件.
如果作為目錄名的話, 一個單獨的點代表當前的工作目錄, 而兩個點表示上一級目錄.
bash$ pwd /home/bozo/projects bash$ cd . bash$ pwd /home/bozo/projects bash$ cd .. bash$ pwd /home/bozo/.
"點"命令[句點, 即.]. 等價于source命令(參見 例子 11-21). 這是一個bash的內建命令.
.
"點"作為文件名的一部分. 如果點放在文件名的開頭的話, 那么這個文件將會成為"隱藏"文件,
并且ls命令將不會正常的顯示出這個文件.
.
"點"字符匹配. 當用作匹配字符的作用時, 通常都是作為正則表達式的一部分來使用, “點"用來
匹配任何的單個字符.
"
部分引用[雙引號, 即”]. "STRING"將會阻止(解釋)STRING中大部分特殊的字符. 參見 5.
’
全引用[單引號, 即’]. 'STRING’將會阻止STRING中所有特殊字符的解釋. 這是一種比使用"更強
烈的形式. 參見 5.
,
逗號操作符. 逗號操作符鏈接了一系列的算術操作. 雖然里邊所有的內容都被運行了,但只有最后
一項被返回.
轉義符[反斜線, 即]. 一種對單字符的引用機制.
\X將會"轉義"字符X. 這等價于"X", 也等價于’X’. \通常用來轉義"和’, 這樣雙引號和但引號就
不會被解釋成特殊含義了.
/
文件名路徑分隔符[斜線, 即/]. 分隔文件名不同的部分(比如 /home/bozo/projects/Makefile).
也可以用來作為除法算術操作符.
命令替換. command結構可以將命令的輸出賦值到一個變量中去. 我們在后邊的后置引用
(backquotes)或后置標記(backticks)中也會講解.
:
空命令[冒號, 即:]. 等價于"NOP" (no op, 一個什么也不干的命令). 也可以被認為與shell的
內建命令true作用相同. ":“命令是一個bash的內建命令, 它的退出碼(exit status)是"true”(0).
死循環:
while : do operation-1 operation-2 ... operation-n done # 與下邊相同: # while true # do # ... # done在if/then中的占位符:
if conditionthen : # 什么都不做,引出分支.elsetake-some-actionfi在一個二元命令中提供一個占位符
: ${username=`whoami`}# ${username=`whoami`} 如果沒有開頭的":"的話, 將會給出一個錯誤,# 除非"username"是一個命令或者內建命令...在here document中提供一個命令所需的占位符.
使用參數替換來評估字符串變量
變量擴展/子串替換.
在與>重定向操作符結合使用時, 將會把一個文件清空, 但是并不會修改這個文件的權限. 如果之
前這個文件并不存在, 那么就創建這個文件.
:在與>>重定向操作符結合使用時, 將不會對預先存在的目標文件(: >> target_file)產生任何影響. 如果這個文件之前并不存在, 那么就創建它.
這只適用于正規文件, 而不適用于管道, 符號連接, 和某些特殊文件.
也可能用來作為注釋行, 雖然我們不推薦這么做. 使用#來注釋的話, 將關閉剩余行的錯誤檢查,
所以可以在注釋行中寫任何東西. 然而, 使用:的話將不會這樣.
!
取反操作符[嘆號, 即!. !操作符將會反轉命令的退出碼的結果,. 也會反轉測試操作符的意義, 比如修改"等號"( = )為"不等號"( != ). !操作符是Bash的關鍵字.
在一個不同的上下文中, !也會出現在變量的間接引用中.
在另一種上下文中, 如命令行模式下, !還能反轉bash的歷史機制 , 需要注意
的是, 在一個腳本中, 歷史機制是被禁用的.
通配符[星號, 即*]. *可以用來做文件名匹配(這個東西有個專有名詞叫globbing)的"通配符".含義是, 可以用來匹配給定目錄下的任何文件名.
bash$ echo *
abs-book.sgml add-drive.sh agram.sh alias.sh
也可以用在正則表達式中, 用來匹配任意個數(包含0個)的字符.
*
算術操作符. 在算術操作符的上下文中, 號表示乘法運算.
如果要做求冪運算, 使用, 這是求冪操作符.
?
測試操作符. 在一個特定的表達式中, ?用來測試一個條件的結果.
在一個雙括號結構中, ?就是C語言的三元操作符. 參見例子 9-31.
在參數替換表達式中, ?用來測試一個變量是否被set了. .
?
通配符. ?在通配(globbing)中, 用來做匹配單個字符的"通配符", 在正則表達式中, 也是用
來表示一個字符.
$
變量替換(引用變量的內容).
在一個變量前面加上$用來引用這個變量的值.
$
行結束符. 在正則表達式中, "$"表示行結束符.
${}
參數替換.
$*, $@
位置參數.
$?
退出狀態碼變量. $? 變量 保存了一個命令, 一個函數, 或者是腳本本身的退出狀態碼.
進程ID變量. 這個$$ 變量 保存了它所在腳本的進程 ID [1]
()
命令組.
在括號中的命令列表, 將會作為一個子shell來運行.
在括號中的變量,由于是在子shell中,所以對于腳本剩下的部分是不可用的.
父進程, 也就是腳本本身, 將不能夠讀取在子進程中創建的變量, 也就是在
子shell中創建的變量.
初始化數組.
Array=(element1 element2 element3){xxx,yyy,zzz,…}
大括號擴展.
一個命令可能會對大括號 [2] 中的以逗號分割的文件列表起作用. (通配(globbing))將對大括號中的文件名做擴展.
在大括號中, 不允許有空白, 除非這個空白被引用或轉義.
{}代碼塊[大括號, 即{}]. 又被稱為內部組, 這個結構事實上創建了一個匿名函數(一個沒有名字的函數). 然而, 與"標準"函數不同的是, 在其中聲明的變量,對于腳本其他部分的代碼來說還是可
見的.
下邊的代碼展示了在大括號結構中代碼的I/O 重定向.
#!/bin/bash# 從/etc/fstab中讀行.File=/etc/fstab#使用大括號和代碼重定向來實現從文件中獲取數據{read line1read line2} < $Fileecho "First line in $File is:"echo "$line1"echoecho "Second line in $File is:"echo "$line2"exit 0將一個代碼塊的結果保存到文件
#!/bin/bash# rpm-check.sh# 這個腳本的目的是為了描述, 列表, 和確定是否可以安裝一個rpm包.# 在一個文件中保存輸出.## 這個腳本使用一個代碼塊來展示.SUCCESS=0E_NOARGS=65if [ -z "$1" ]thenecho "Usage: `basename $0` rpm-file"exit $E_NOARGSfi{echoecho "Archive Description:"rpm -qpi $1 # 查詢說明.echoecho "Archive Listing:"rpm -qpl $1 # 查詢列表.echorpm -i --test $1 # 查詢rpm包是否可以被安裝.if [ "$?" -eq $SUCCESS ]thenecho "$1 can be installed."elseecho "$1 cannot be installed."fiecho} > "$1.test" # 把代碼塊中的所有輸出都重定向到文件中.echo "Results of rpm test in file $1.test"# 查看rpm的man頁來查看rpm的選項.exit 0與上面所講到的()中的命令組不同的是, {大括號}中的代碼塊將不會開啟一
個新的子shell. [3]
“{} \;”
路徑名. 一般都在find命令中使用. 這不是一個shell內建命令.
";"用來結束find命令序列的-exec選項. 它需要被保護以防止被shell所解
釋.
[ ]
條件測試.
條件測試表達式放在[ ]中. 值得注意的是[是shell內建test命令的一部分, 并不
是/usr/bin/test中的外部命令的一個鏈接.
[[ ]]
測試.
測試表達式放在[[ ]]中. (shell關鍵字).
[ ]
數組元素.
在一個array結構的上下文中, 中括號用來引用數組中每個元素的編號.
[ ]
字符范圍.
用作正則表達式的一部分, 方括號描述一個匹配的字符范圍.
(( ))
整數擴展.
擴展并計算在(( ))中的整數表達式.
請參考關于(( … )) 結構的討論.
重定向.
scriptname >filename 重定向scriptname的輸出到文件filename中. 如果filename存在的話, 那
么將會被覆蓋.
command &>filename 重定向command的stdout和stderr到filename中.
command >&2 重定向command的stdout到stderr中.
scriptname >>filename 把scriptname的輸出追加到文件filename中. 如果filename不存在的話,
將會被創建.
[i]<>filename 打開文件filename用來讀寫, 并且分配文件描述符i給這個文件. 如果filename不
存在, 這個文件將會被創建.
進程替換.
(command)>
<(command)
在一種不同的上下文中, "<“和”>"可用來做 字符串比較操作.
在另一種上下文中, "<“和”>"可用來做 整數比較操作. 參見例子 12-9.
<<
用在here document中的重定向.
<<<
用在here string中的重定向.
<, >
ASCII comparison.
正則表達式中的單詞邊界 .
bash$ grep '\<the\>' textfile|
管道. 分析前邊命令的輸出, 并將輸出作為后邊命令的輸入. 這是一種產生命令鏈的好方法.
管道是進程間通訊的一個典型辦法, 將一個進程的stdout放到另一個進程的stdin中. 標準的方法是將一個一般命令的輸出, 比如cat或者echo, 傳遞到一個 “過濾命令”(在這個過濾命令中將處理輸入)中, 然后得到結果.
cat $filename1 $filename2 | grep $search_word當然輸出的命令也可以傳遞到腳本中.
#!/bin/bash# uppercase.sh : 修改輸入, 全部轉換為大寫.tr 'a-z' 'A-Z'# 字符范圍必須被""引用起來#+ 來阻止產生單字符的文件名.exit 0現在讓我們輸送ls -l的輸出到一個腳本中.
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管道中的每個進程的stdout比須被下一個進程作為stdin來讀入. 否則, 數
據流會阻塞, 并且管道將產生一些非預期的行為.
如果管道中的某個命令產生了一個異常,并中途失敗,那么這個管道將過早的
終止. 這種行為被叫做broken pipe, 并且這種狀態下將發送一個SIGPIPE
信號.
強制重定向(即使設置了noclobber選項 – 就是-C選項). 這將強制的覆蓋一個現存文件.
||或-邏輯操作. 在一個條件測試結構中, 如果條件測試結構兩邊中的任意一邊結果為true的話,||操作就會返回0(代表執行成功). &后臺運行命令. 一個命令后邊跟一個& 表示在后臺運行.
bash$ sleep 10 & [1] 850 [1]+ Done sleep 10 在一個腳本中,命令和循環都可能運行在后臺.#!/bin/bash# background-loop.shfor i in 1 2 3 4 5 6 7 8 9 10 # 第一個循環.doecho -n "$i "done & # 在后臺運行這個循環.# 在第2個循環之后, 將在某些時候執行.echo # 這個'echo'某些時候將不會顯示.for i in 11 12 13 14 15 16 17 18 19 20 # 第二個循環.doecho -n "$i "doneecho # 這個'echo'某些時候將不會顯示.# ======================================================# 期望的輸出應該是:# 1 2 3 4 5 6 7 8 9 10# 11 12 13 14 15 16 17 18 19 20# 然而實際的結果有可能是: # 11 12 13 14 15 16 17 18 19 20# 1 2 3 4 5 6 7 8 9 10 bozo $# (第2個'echo'沒執行, 為什么?)# 也可能是:# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20# (第1個'echo'沒執行, 為什么?)# 非常少見的執行結果, 也有可能是:# 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20# 前臺的循環先于后臺的執行.exit 0# Nasimuddin Ansari 建議加一句 sleep 1#+ 在6行和14行的 echo -n "$i" 之后加這句.#+ 為了真正的樂趣.在一個腳本內后臺運行一個命令,有可能造成這個腳本的掛起,等待一個按鍵
響應. 幸運的是, 我們有針對這個問題的解決辦法.
與-邏輯操作. 在一個條件測試結構中, 只有在條件測試結構的兩邊結果都為true的時候, &&操作才會返回0(代表sucess).
-選項, 前綴. 在所有的命令內如果想使用選項參數的話,前邊都要加上"-".
COMMAND -[Option1][Option2][...] ls -al sort -dfu $filename set -- $variableif [ $file1 -ot $file2 ]then echo "File $file1 is older than $file2."fiif [ "$a" -eq "$b" ]then echo "$a is equal to $b."fiif [ "$c" -eq 24 -a "$d" -eq 47 ]thenecho "$c equals 24 and $d equals 47."fi -用于重定向stdin或stdout[破折號, 即-].
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)# 從一個目錄移動整個目錄樹到另一個目錄# [感謝Alan Cox <a.cox@swansea.ac.uk>, 走出了部分修改]# 1) cd /source/directory 源目錄# 2) && "與列表": 如果'cd'命令成功了, 那么就執行下邊的命令.# 3) tar cf - . 'c'創建一個新文檔, 'f'后邊跟'-'指定目標文件作為stdout# '-'后邊的'f'(file)選項, 指明作為stdout的目文件. # 并且在當前目錄('.')執行.# 4) | 管道...# 5) ( ... ) 一個子shell# 6) cd /dest/directory 改變當前目錄到目標目錄.# 7) && "與列表", 同上# 8) tar xpvf - 'x'解檔, 'p'保證所有權和文件屬性,# 'v'發完整消息到stdout,# 'f'后邊跟'-',從stdin讀取數據.## :'x' , 'p', 'v', 'f' .注意是一個命令是選項# Whew!# 更優雅的寫法應該是:# cd source/directory# tar cf - . | (cd ../dest/directory; tar xpvf -)## 當然也可以這么寫:# cp -a /source/directory/* /dest/directory# 或者: # cp -a /source/directory/* /source/directory/.[^.]* /dest/directory# 如果在/source/directory中有隱藏文件的話.bunzip2 -c linux-2.6.16.tar.bz2 | tar xvf -# --未解壓的tar文件-- | --然后把它傳遞到"tar"中--# 如果 "tar" 沒能夠正常的處理"bunzip2",#+ 這就需要使用管道來執行2個單獨的步驟來完成它.# 這個練習的目的是解檔"bzipped"的kernel源文件.
注意, 在這個上下文中"-"本身并不是一個Bash操作, 而是一個可以被特定的UNIX工具識別的選
項, 這些特定的UNIX工具特指那些可以寫輸出到stdout的工具, 比如tar, cat, 等等.
在需要一個文件名的位置, -重定向輸出到stdout(有時候會在tar和cf中出現), 或者從stdin接受輸入, 而不是從一個文件中接受輸入. 這是在管道中使用文件導向(file-oriented)工具來作為過濾器的一種方法. bash$ file Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file... 在命令行上單獨給出一個file, 會給出一個錯誤信息. 添加一個"-"將得到一個更有用的結果. 這會使shell等待用戶輸入. bash$ file - abc standard input: ASCII text bash$ file - #!/bin/bash standard input: Bourne-Again shell script text executable 現在命令從stdin中接受了輸入, 并分析它. "-"可以被用來將stdout通過管道傳遞到其他命令中. 這樣就允許使用在一個文件開頭添加幾行的技巧.
使用diff命令來和另一個文件的某一段進行比較:
grep Linux file1 | diff file2 -最后, 來展示一個使用-的tar命令的一個真實的例子.
備份最后一天所有修改的文件
控制字符在腳本中不能正常使用.
Ctl-B
退格(非破壞性的), 就是退格但是不刪掉前面的字符.
Ctl-C
break. 終結一個前臺作業.
Ctl-D
從一個shell中登出(與exit很相像).
“EOF”(文件結束). 這也能從stdin中終止輸入.
在console或者在xterm窗口中輸入的時候, Ctl-D將刪除光標下字符. 當沒有字符時, Ctl-
D將退出當前會話, 在一個xterm窗口中, 則會產生關閉此窗口的效果.
Ctl-G
“嗶” (beep). 在一些老式的打字機終端上, 它會響一下鈴.
Ctl-H
“退格”(破壞性的), 就是在退格之后, 還要刪掉前邊的字符.
Ctl-I
水平制表符.
Ctl-J
重起一行(換一行并到行首). 在腳本中, 也可以使用8進制表示法 – ‘\012’ 或者16進制
表示法 – ‘\x0a’ 來表示.
Ctl-K
垂直制表符.
當在console或者xterm窗口中輸入文本時, Ctl-K將會刪除從光標所在處到行為的全部字
符. 在腳本中, Ctl-K的行為有些不同, 具體請參見下邊的Lee Maschmeyer的例子程序.
Ctl-L
清屏(清除終端的屏幕顯示). 在終端中, 與clear命令的效果相同. 當發送到打印機上時,
Ctl-L會讓打印機將打印紙卷到最后.
Ctl-M
回車.
Ctl-Q
恢復(XON).
在一個終端中恢復stdin.
Ctl-S
掛起(XOFF).
在一個終端中凍結stdin. (使用Ctl-Q可以恢復輸入.)
Ctl-U
刪除光標到行首的所有字符. 在某些設置下, 不管光標的所在位置Ctl-U都將刪除整行輸
入.
Ctl-V
當輸入字符時, Ctl-V允許插入控制字符. 比如, 下邊的兩個例子是等價的:
1 echo -e ‘\x0a’
2 echo
Ctl-V主要用于文本編輯.
Ctl-W
當在控制臺或一個xterm窗口敲入文本時, Ctl-W將會刪除當前光標到左邊最近一個空格間
的全部字符. 在某些設置下, Ctl-W將會刪除當前光標到左邊第一個非字母或數字之間的全
部字符.
Ctl-Z
暫停前臺作業.
空白
用來分隔函數, 命令或變量. . 空白包含空格, tab, 空行, 或者是它們之間任意的組合體. [4]
在某些上下文中, 比如變量賦值, 空白是不被允許的, 會產生語法錯誤.
空行不會影響腳本的行為, 因此使用空行可以很好的劃分獨立的函數段以增加可讀性.
特殊變量$IFS用來做一些輸入命令的分隔符, 默認情況下是空白.
如果想在字符串或變量中使用空白, 那么應該使用引用.
注意事項
[1] PID, 或進程 ID, 是分配給運行進程的一個數字. 要想察看所有運行進程的PID可以使
用ps命令.
[2] The shell does the brace expansion. The command itself acts upon the result of
the expansion.
[3] 例外: 在pipe中的一個大括號中的代碼段可能運行在一個 子shell中.
[4] 一個換行符(“重起一行”)也被認為是空白符. 這也就解釋了為什么一個只包含換行符的空
行也被認為是空白.
總結
以上是生活随笔為你收集整理的bash--shell高级编程-特殊字符的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多位诺贝尔奖获得者推荐的精品力作:《大数
- 下一篇: 基于主动学习和克里金插值的空气质量推测