七種文件類型... 3
正則表達式... 3
字符類描述... 4
shell的引號類型... 4
變量設(shè)置時的不同模式:... 4
條件測試... 5
命令執(zhí)行順序... 6
腳本調(diào)試... 6
一些常用的小trick.. 6
打印一些頭信息... 6
創(chuàng)建一個長度為0的空文件... 7
一些常用的shell變量... 7
$0的使用... 8
Shift的運用... 8
用head或tail指令指定查閱的行數(shù)... 8
awk使用規(guī)則... 8
第一個 awk. 8
多個字段... 9
外部腳本... 10
BEGIN 和 END 塊... 10
規(guī)則表達式和塊... 11
條件語句... 11
數(shù)值變量... 12
字符串化變量... 13
眾多運算符... 13
字段分隔符... 13
字段數(shù)量... 14
記錄號... 14
多行記錄... 15
OFS 和 ORS. 16
將多行轉(zhuǎn)換成用 tab 分隔的格式... 17
循環(huán)結(jié)構(gòu)... 18
for 循環(huán)... 18
break 和 continue. 18
數(shù)組下標字符串化... 20
數(shù)組工具... 21
格式化輸出... 22
字符串函數(shù)... 22
一些更耐人尋味的函數(shù)... 23
字符串替換... 24
特殊字符串形式... 24
sed使用規(guī)則... 25
sed 示例... 25
另一個 sed 示例... 25
地址范圍... 26
帶規(guī)則表達式的地址... 26
有關(guān)地址的更多內(nèi)容... 27
替換... 27
規(guī)則表達式混亂... 28
更多字符匹配... 29
高級替換功能... 29
組合使用... 30
一個地址的多個命令... 30
附加、插入和更改行... 31
使用 sed 的幾個示例... 31
linux常用腳本和函數(shù)... 33
linux常用命令... 38
關(guān)于文件/目錄處理的指令: 38
關(guān)于 Process 處理的指令: 40
關(guān)于字符串處理的指令: 41
聯(lián)機查詢的指令: 41
網(wǎng)絡(luò)運用指令: 42
Vi常用技巧... 44
Shell腳本編程的常識
(這些往往是經(jīng)常用到,但是各種網(wǎng)絡(luò)上的材料都語焉不詳?shù)臇|西,個人認為比較有用)
七種文件類型
d 目錄 l 符號鏈接
s 套接字文件 b 塊設(shè)備文件
c 字符設(shè)備文件 p 命名管道文件
- 普通文件
正則表達式
從一個文件或命令輸出中抽取或過濾文本時。可使用正則表達式(RE),正則表達式是一些特殊或不很特殊的字符串模式的集合。
基本的元字符集:
^ 只匹配行首。
$ 只匹配行尾。
* 一個單字符后緊跟*,匹配0個或多個此單字符。
[] 匹配[]內(nèi)字符,可以是一個單字符,也可以是字符序列??梢允?br />
用-來表示[]內(nèi)范圍,如[1-5]等價于[1,2,3,4,5]。
屏蔽一個元字符的特殊含義,如$表示字符$,而不表示匹配行
尾。
. 匹配任意單字符。
pattern{n} 匹配pattern出現(xiàn)的次數(shù)n
pattern{n,}m匹配pattern出現(xiàn)的次數(shù),但表示次數(shù)最少為n
pattern{n,m} 匹配pattern出現(xiàn)的次數(shù)在n與m之間(n,m為0-255)
幾個常見的例子:
顯示可執(zhí)行的文件:ls –l | grep …x...x..x
只顯示文件夾:ls –l | grep ^d
匹配所有的空行:^$
匹配所有的單詞:[A-Z a-z]*
匹配任一非字母型字符:[^A-Z a-z]
包含八個字符的行:^……..$(8個.)
字符類描述
以下是可用字符類的相當完整的列表:
[:alnum:] 字母數(shù)字 [a-z A-Z 0-9]
[:alpha:] 字母 [a-z A-Z]
[:blank:] 空格或制表鍵
[:cntrl:] 任何控制字符
[:digit:] 數(shù)字 [0-9]
[:graph:] 任何可視字符(無空格)
[:lower:] 小寫 [a-z]
[:print:] 非控制字符
[:punct:] 標點字符
[:space:] 空格
[:upper:] 大寫 [A-Z]
[:xdigit:] 十六進制數(shù)字 [0-9 a-f A-F]
盡可能使用字符類是很有利的,因為它們可以更好地適應(yīng)非英語 locale(包括某些必需的重音字符等等).
shell的引號類型
shell共有四種引用類型:
“ ” 雙引號
‘ ’ 單引號
` ` 反引號
反斜線
l “ ” 可引用除$、` 、 、外的任意字符或字符串,“ ”中的變量能夠正常顯示變量值。
l ‘ ’與“ ”類似,不同在于shell會忽略任何的引用值。
例如: GIRL=‘girl’
echo “The ‘$GIRL’ did well”
則打?。篢he ‘girl’ did well
l ` `用于設(shè)置系統(tǒng)命令的輸出到變量,shell會將` `中的內(nèi)容作為一個系統(tǒng)命令并執(zhí)行質(zhì)。
例如:echo `date` 則打印當前的系統(tǒng)時間。
l 用來屏蔽特殊含義的字符:& * + ^ $ ` “ | ?
例如:expr 12 * 12 將輸出144
變量設(shè)置時的不同模式:
valiable_name=value 設(shè)置實際值到 variable_name中
valiable_name+value 如果設(shè)置了variable_name,則重設(shè)其值
valiable_name:?value 如果未設(shè)置variable_name,則先顯示未定義用戶錯誤信息
valiable_name?value 如果未設(shè)置variable_name,則顯示系統(tǒng)錯誤信息
valiable_name:=value 如果未設(shè)置variable_name,則設(shè)置其值
valiable_name-value 同上,但取值并不設(shè)置到variable_name
條件測試
test命令用于測試字符串、文件狀態(tài)和數(shù)字,expr測試和執(zhí)行數(shù)值輸出。
Test格式:test condition 或 [ condition ](需要特別注意的是condition的兩邊都要有一個空格,否則會報錯),test命令返回0表示成功。
l 下面將分別描述test的三種測試:
n 文件狀態(tài)測試(常用的)
-d 測試是否文件夾
-f 測試是否一般文件
-L 測試是否鏈接文件
-r 測試文件是否可讀
-w 測試文件是否可寫
-x 測試文件是否可執(zhí)行
-s 測試文件是否非空
n 字符串測試
五種格式: test “string”
test string_operator “string”
test “string” string_operator “string”
[ string_operator “string” ]
[ “string” string_operator “string” ]
其中string_operator可以為: = 兩字符串相等
!= 兩字符串不等
-z 空串
-n 非空串
n 數(shù)值測試
兩種格式: “number” number_operator “number”
[ “number” number_operator “number” ]
其中:number_operator 可以為:-eq 、-ne、-gt、-lt、-ge
例如: NUMBER=130
[ “990” –le “995” –a “NUMBER” -gt “133” ]
(其中-a表示前后結(jié)果相“與”)
l expr命令一般用于整數(shù)值,但也可以用于字符串。
n 格式: expr srgument operator operator argument
例如: expr 10 + 10
expr 10 ^ 2 (10的平方)
expr $value + 10
n 增量計數(shù)――expr在循環(huán)中最基本的用法
例如: LOOP=0
LOOP=`expr $LOOP + 1`
n 模式匹配:通過指定的冒號選項計算字符串中的字符數(shù)
例如: value=account.doc
expr $value : `(.*).doc`
輸出 account
命令執(zhí)行順序
&& 成功執(zhí)行一個命令后再執(zhí)行下一個
|| 一個命令執(zhí)行失敗后再執(zhí)行另一個命令
( ) 在當前shell中執(zhí)行一組命令(格式:(命令1;命令2; ……))
{ } 同( )
例如: comet mouth_end || ( echo “hello” | mail dave ;exit )
如果沒有( ),則shell將直接執(zhí)行最后一個命令(exit)
腳本調(diào)試
最有用的調(diào)試腳本的工具是echo命令,可以隨時打印有關(guān)變量或操作的信息,以幫助定位錯誤。也可使用打印最后狀態(tài)($?) 命令來判斷命令是否成功,這時要注意的是要在執(zhí)行完要測試的命令后立即輸出$?,否則$?將會改變。
Set命令也可以用來輔助腳本測試:
Set –n 讀命令但是不執(zhí)行
Set –v 顯示讀取的所有的行
Set –x 顯示所有的命令及其參數(shù)
(要關(guān)閉set選項,只要把-換成+就可以了,這里有點特殊,要注意一下)
一些常用的小trick
打印一些頭信息
command << dilimiter
……
……
dilimiter
以分界符號dilimiter中的內(nèi)容作為命令的標準輸入
常用在echo命令中,這樣就避免了沒輸出一行就要使用一個echo命令,同時,輸出格式的調(diào)整也相應(yīng)變得簡單了。
例如: echo << something_message
************************************************
hello, welcome to use my shell script
************************************************
something_message
將在屏幕上輸出:
************************************************
hello, welcome to use my shell script
************************************************
一、利用<<的分解符號性質(zhì)還可以自動選擇菜單或?qū)崿F(xiàn)自動的ftp傳輸
也就是利用分解符號的性質(zhì)自動選擇菜單。
例如: ./menu_choose >>output_file 2>&1 <<Choose
2
3
Y
Choose
則自動在執(zhí)行腳本的過程中一步步作出選擇:2,3,Y
<<這種性質(zhì)決定了它是理想的訪問數(shù)據(jù)庫的有用工具,可以用它來輸入面對數(shù)據(jù)庫提示時所作的各種選擇。
創(chuàng)建一個長度為0的空文件
執(zhí)行 > file_name 命令或 touch file_name 命令。
一些常用的shell變量
$# 傳遞到腳本的參數(shù)個數(shù)
$* 以一個單字符串顯示所有向腳本傳遞的參數(shù)(可大于9個)
$$ 腳本運行的當前進程的ID號
$! 后臺運行的最后一個進程的ID號
$@ 與$#相同,但使用時加引號,并在引號中返回每個參數(shù)
$- 顯示shell使用的當前選項
$? 顯示最后命令的退出狀態(tài),0表示無錯誤(這個變量也常常用來打印輸出,在腳本調(diào)試時標記某個shell命令或某個函數(shù)是否正確執(zhí)行,但是要注意,$?記載的是最近的函數(shù)或命令的退出狀態(tài),因此打印時應(yīng)該立即打印以獲得正確的信息)
$0的使用
在變量中有一種位置變量$n,用來存放函數(shù)調(diào)用或腳本執(zhí)行時傳入的參數(shù),其中$0表示函數(shù)名或腳本名,需要注意的是,這時的腳本名傳遞的是包含全路徑的腳本名。從$1-$9表示傳入的第一到第九個參數(shù),這樣的參數(shù)表示不能多于九個,如果多于九個,可以使用下面將要提到的shift指令來讀取。
因為$0存放函數(shù)名或腳本名,因此我們可以通過echo $0來輸出調(diào)用信息,但是,由于存放的是全路徑名,我們可以利用一個shell命令來得到腳本名,basename $0 將得到$0中名字的部分,而與之相反的,dirname $0將得到$0中路徑的部分。
Shift的運用
用head或tail指令指定查閱的行數(shù)
例如:查閱文件前20行: head –20 file_name
查閱文件后10行: tail –10 file_name
awk使用規(guī)則
awk 是一種很棒的語言。awk 適合于文本處理和報表生成,它還有許多精心設(shè)計的特性,允許進行需要特殊技巧程序設(shè)計。與某些語言不同,awk 的語法較為常見。它借鑒了某些語言的一些精華部分,如 C 語言、python 和 bash(雖然在技術(shù)上,awk 比 python 和 bash 早創(chuàng)建)。awk 是那種一旦學會了就會成為您戰(zhàn)略編碼庫的主要部分的語言。
第一個 awk
讓我們繼續(xù),開始使用 awk,以了解其工作原理。在命令行中輸入以下命令:
$ awk '{ print }' /etc/passwd
您將會見到 /etc/passwd 文件的內(nèi)容出現(xiàn)在眼前?,F(xiàn)在,解釋 awk 做了些什么。調(diào)用 awk 時,我們指定 /etc/passwd 作為輸入文件。執(zhí)行 awk 時,它依次對 /etc/passwd 中的每一行執(zhí)行 print 命令。所有輸出都發(fā)送到 stdout,所得到的結(jié)果與與執(zhí)行catting /etc/passwd完全相同。
現(xiàn)在,解釋 { print } 代碼塊。在 awk 中,花括號用于將幾塊代碼組合到一起,這一點類似于 C 語言。在代碼塊中只有一條 print 命令。在 awk 中,如果只出現(xiàn) print 命令,那么將打印當前行的全部內(nèi)容。
這里是另一個 awk 示例,它的作用與上例完全相同:
$ awk '{ print $0 }' /etc/passwd
在 awk 中,$0 變量表示整個當前行,所以 print 和 print $0 的作用完全一樣。
如果您愿意,可以創(chuàng)建一個 awk 程序,讓它輸出與輸入數(shù)據(jù)完全無關(guān)的數(shù)據(jù)。以下是一個示例:
$ awk '{ print "" }' /etc/passwd
只要將 "" 字符串傳遞給 print 命令,它就會打印空白行。如果測試該腳本,將會發(fā)現(xiàn)對于 /etc/passwd 文件中的每一行,awk 都輸出一個空白行。再次說明, awk 對輸入文件中的每一行都執(zhí)行這個腳本。以下是另一個示例:
$ awk '{ print "hiya" }' /etc/passwd
運行這個腳本將在您的屏幕上寫滿 hiya。:)
多個字段
awk 非常善于處理分成多個邏輯字段的文本,而且讓您可以毫不費力地引用 awk 腳本中每個獨立的字段。以下腳本將打印出您的系統(tǒng)上所有用戶帳戶的列表:
$ awk -F":" '{ print $1 }' /etc/passwd
上例中,在調(diào)用 awk 時,使用 -F 選項來指定 ":" 作為字段分隔符。awk 處理 print $1 命令時,它會打印出在輸入文件中每一行中出現(xiàn)的第一個字段。以下是另一個示例:
$ awk -F":" '{ print $1 $3 }' /etc/passwd
以下是該腳本輸出的摘錄:
halt7
operator11
root0
shutdown6
sync5
bin1
....etc.
如您所見,awk 打印出 /etc/passwd 文件的第一和第三個字段,它們正好分別是用戶名和用戶標識字段。現(xiàn)在,當腳本運行時,它并不理想 -- 在兩個輸出字段之間沒有空格!如果習慣于使用 bash 或 python 進行編程,那么您會指望 print $1 $3 命令在兩個字段之間插入空格。然而,當兩個字符串在 awk 程序中彼此相鄰時,awk 會連接它們但不在它們之間添加空格。以下命令會在這兩個字段中插入空格:
$ awk -F":" '{ print $1 " " $3 }' /etc/passwd
以這種方式調(diào)用 print 時,它將連接 $1、" " 和 $3,創(chuàng)建可讀的輸出。當然,如果需要的話,我們還可以插入一些文本標簽:
$ awk -F":" '{ print "username: " $1 "ttuid:" $3" }' /etc/passwd
這將產(chǎn)生以下輸出:
username: halt uid:7
username: operator uid:11
username: root uid:0
username: shutdown uid:6
username: sync uid:5
username: bin uid:1
....etc.
外部腳本
將腳本作為命令行自變量傳遞給 awk 對于小的單行程序來說是非常簡單的,而對于多行程序,它就比較復雜。您肯定想要在外部文件中撰寫腳本。然后可以向 awk 傳遞 -f 選項,以向它提供此腳本文件:
$ awk -f myscript.awk myfile.in
將腳本放入文本文件還可以讓您使用附加 awk 功能。例如,這個多行腳本與前面的單行腳本的作用相同,它們都打印出 /etc/passwd 中每一行的第一個字段:
BEGIN {
FS=":"
}
{ print $1 }
這兩個方法的差別在于如何設(shè)置字段分隔符。在這個腳本中,字段分隔符在代碼自身中指定(通過設(shè)置 FS 變量),而在前一個示例中,通過在命令行上向 awk 傳遞 -F":" 選項來設(shè)置 FS。通常,最好在腳本自身中設(shè)置字段分隔符,只是因為這表示您可以少輸入一個命令行自變量。我們將在本文的后面詳細討論 FS 變量。
BEGIN 和 END 塊
通常,對于每個輸入行,awk 都會執(zhí)行每個腳本代碼塊一次。然而,在許多編程情況中,可能需要在 awk 開始處理輸入文件中的文本之前執(zhí)行初始化代碼。對于這種情況,awk 允許您定義一個 BEGIN 塊。我們在前一個示例中使用了 BEGIN 塊。因為 awk 在開始處理輸入文件之前會執(zhí)行 BEGIN 塊,因此它是初始化 FS(字段分隔符)變量、打印頁眉或初始化其它在程序中以后會引用的全局變量的極佳位置。
awk 還提供了另一個特殊塊,叫作 END 塊。awk 在處理了輸入文件中的所有行之后執(zhí)行這個塊。通常,END 塊用于執(zhí)行最終計算或打印應(yīng)該出現(xiàn)在輸出流結(jié)尾的摘要信息。
規(guī)則表達式和塊
awk 允許使用規(guī)則表達式,根據(jù)規(guī)則表達式是否匹配當前行來選擇執(zhí)行獨立代碼塊。以下示例腳本只輸出包含字符序列 foo 的那些行:
/foo/ { print }
當然,可以使用更復雜的規(guī)則表達式。以下腳本將只打印包含浮點數(shù)的行:
/[0-9]+.[0-9]*/ { print }
還有許多其它方法可以選擇執(zhí)行代碼塊。我們可以將任意一種布爾表達式放在一個代碼塊之前,以控制何時執(zhí)行某特定塊。僅當對前面的布爾表達式求值為真時,awk 才執(zhí)行代碼塊。以下示例腳本輸出將輸出其第一個字段等于 fred 的所有行中的第三個字段。如果當前行的第一個字段不等于 fred,awk 將繼續(xù)處理文件而不對當前行執(zhí)行 print 語句:
$1 == "fred" { print $3 }
awk 提供了完整的比較運算符集合,包括 "=="、"<"、">"、"<="、">=" 和 "!="。另外,awk 還提供了 "~" 和 "!~" 運算符,它們分別表示“匹配”和“不匹配”。它們的用法是在運算符左邊指定變量,在右邊指定規(guī)則表達式。如果某一行的第五個字段包含字符序列 root,那么以下示例將只打印這一行中的第三個字段:
$5 ~ /root/ { print $3 }
條件語句
awk 還提供了非常好的類似于 C 語言的 if 語句。如果您愿意,可以使用 if 語句重寫前一個腳本:
{
if ( $5 ~ /root/ ) {
print $3
}
}
這兩個腳本的功能完全一樣。第一個示例中,布爾表達式放在代碼塊外面。而在第二個示例中,將對每一個輸入行執(zhí)行代碼塊,而且我們使用 if 語句來選擇執(zhí)行 print 命令。這兩個方法都可以使用,可以選擇最適合腳本其它部分的一種方法。
以下是更復雜的 awk if 語句示例??梢钥吹?,盡管使用了復雜、嵌套的條件語句,if 語句看上去仍與相應(yīng)的 C 語言 if 語句一樣:
{
if ( $1 == "foo" ) {
if ( $2 == "foo" ) {
print "uno"
} else {
print "one"
}
} else if ($1 == "bar" ) {
print "two"
} else {
print "three"
}
}
使用 if 語句還可以將代碼:
! /matchme/ { print $1 $3 $4 }
轉(zhuǎn)換成:
{
if ( $0 !~ /matchme/ ) {
print $1 $3 $4
}
}
這兩個腳本都只輸出不包含 matchme 字符序列的那些行。此外,還可以選擇最適合您的代碼的方法。它們的功能完全相同。
awk 還允許使用布爾運算符 "||"(邏輯與)和 "&&"(邏輯或),以便創(chuàng)建更復雜的布爾表達式:
( $1 == "foo" ) && ( $2 == "bar" ) { print }
這個示例只打印第一個字段等于 foo 且第二個字段等于 bar 的那些行。
數(shù)值變量
至今,我們不是打印字符串、整行就是特定字段。然而,awk 還允許我們執(zhí)行整數(shù)和浮點運算。通過使用數(shù)學表達式,可以很方便地編寫計算文件中空白行數(shù)量的腳本。以下就是這樣一個腳本:
BEGIN { x=0 }
/^$/ { x=x+1 }
END { print "I found " x " blank lines. :}" }
在 BEGIN 塊中,將整數(shù)變量 x 初始化成零。然后,awk 每次遇到空白行時,awk 將執(zhí)行 x=x+1 語句,遞增 x。處理完所有行之后,執(zhí)行 END 塊,awk 將打印出最終摘要,指出它找到的空白行數(shù)量。
字符串化變量
awk 的優(yōu)點之一就是“簡單和字符串化”。我認為 awk 變量“字符串化”是因為所有 awk 變量在內(nèi)部都是按字符串形式存儲的。同時,awk 變量是“簡單的”,因為可以對它執(zhí)行數(shù)學操作,且只要變量包含有效數(shù)字字符串,awk 會自動處理字符串到數(shù)字的轉(zhuǎn)換步驟。要理解我的觀點,請研究以下這個示例:
x="1.01"
# We just set x to contain the *string* "1.01"
x=x+1
# We just added one to a *string*
print x
# Incidentally, these are comments :)
awk 將輸出:
2.01
有趣吧!雖然將字符串值 1.01 賦值給變量 x,我們?nèi)匀豢梢詫λ右?。但?bash 和 python 中卻不能這樣做。首先,bash 不支持浮點運算。而且,如果 bash 有“字符串化”變量,它們并不“簡單”;要執(zhí)行任何數(shù)學操作,bash 要求我們將數(shù)字放到丑陋的 $( ) ) 結(jié)構(gòu)中。如果使用 python,則必須在對 1.01 字符串執(zhí)行任何數(shù)學運算之前,將它轉(zhuǎn)換成浮點值。雖然這并不困難,但它仍是附加的步驟。如果使用 awk,它是全自動的,而那會使我們的代碼又好又整潔。如果想要對每個輸入行的第一個字段乘方并加一,可以使用以下腳本:
{ print ($1^2)+1 }
如果做一個小實驗,就可以發(fā)現(xiàn)如果某個特定變量不包含有效數(shù)字,awk 在對數(shù)學表達式求值時會將該變量當作數(shù)字零處理。
眾多運算符
awk 的另一個優(yōu)點是它有完整的數(shù)學運算符集合。除了標準的加、減、乘、除,awk 還允許使用前面演示過的指數(shù)運算符 "^"、模(余數(shù))運算符 "%" 和其它許多從 C 語言中借入的易于使用的賦值操作符。
這些運算符包括前后加減(i++、--foo)、加/減/乘/除賦值運算符( a+=3、b*=2、c/=2.2、d-=6.2)。不僅如此 -- 我們還有易于使用的模/指數(shù)賦值運算符(a^=2、b%=4)。
字段分隔符
awk 有它自己的特殊變量集合。其中一些允許調(diào)整 awk 的運行方式,而其它變量可以被讀取以收集關(guān)于輸入的有用信息。我們已經(jīng)接觸過這些特殊變量中的一個,F(xiàn)S。前面已經(jīng)提到過,這個變量讓您可以設(shè)置 awk 要查找的字段之間的字符序列。我們使用 /etc/passwd 作為輸入時,將 FS 設(shè)置成 ":"。當這樣做有問題時,我們還可以更靈活地使用 FS。
FS 值并沒有被限制為單一字符;可以通過指定任意長度的字符模式,將它設(shè)置成規(guī)則表達式。如果正在處理由一個或多個 tab 分隔的字段,您可能希望按以下方式設(shè)置 FS:
FS="t+"
以上示例中,我們使用特殊 "+" 規(guī)則表達式字符,它表示“一個或多個前一字符”。
如果字段由空格分隔(一個或多個空格或 tab),您可能想要將 FS 設(shè)置成以下規(guī)則表達式:
FS="[[:space:]+]"
這個賦值表達式也有問題,它并非必要。為什么?因為缺省情況下,F(xiàn)S 設(shè)置成單一空格字符,awk 將這解釋成表示“一個或多個空格或 tab”。在這個特殊示例中,缺省 FS 設(shè)置恰恰是您最想要的!
復雜的規(guī)則表達式也不成問題。即使您的記錄由單詞 "foo" 分隔,后面跟著三個數(shù)字,以下規(guī)則表達式仍允許對數(shù)據(jù)進行正確的分析:
FS="foo[0-9][0-9][0-9]"
字段數(shù)量
接著我們要討論的兩個變量通常并不是需要賦值的,而是用來讀取以獲取關(guān)于輸入的有用信息。第一個是 NF 變量,也叫做“字段數(shù)量”變量。awk 會自動將該變量設(shè)置成當前記錄中的字段數(shù)量??梢允褂?NF 變量來只顯示某些輸入行:
NF == 3 { print "this particular record has three fields: " $0 }
當然,也可以在條件語句中使用 NF 變量,如下:
{
if ( NF > 2 ) {
print $1 " " $2 ":" $3
}
}
記錄號
記錄號 (NR) 是另一個方便的變量。它始終包含當前記錄的編號(awk 將第一個記錄算作記錄號 1)。迄今為止,我們已經(jīng)處理了每一行包含一個記錄的輸入文件。對于這些情況,NR 還會告訴您當前行號。然而,當我們在本系列以后部分中開始處理多行記錄時,就不會再有這種情況,所以要注意!可以象使用 NF 變量一樣使用 NR 來只打印某些輸入行:
(NR < 10 ) || (NR > 100) { print "We are on record number 1-9 or 101+" }
另一個示例:
{
#skip header
if ( NR > 10 ) {
print "ok, now for the real information!"
}
}
awk 提供了適合各種用途的附加變量。我們將在以后的文章中討論這些變量。
多行記錄
awk 是一種用于讀取和處理結(jié)構(gòu)化數(shù)據(jù)(如系統(tǒng)的 /etc/passwd 文件)的極佳工具。/etc/passwd 是 UNIX 用戶數(shù)據(jù)庫,并且是用冒號定界的文本文件,它包含許多重要信息,包括所有現(xiàn)有用戶帳戶和用戶標識,以及其它信息。在我的前一篇文章中,我演示了 awk 如何輕松地分析這個文件。我們只須將 FS(字段分隔符)變量設(shè)置成 ":"。
正確設(shè)置了 FS 變量之后,就可以將 awk 配置成分析幾乎任何類型的結(jié)構(gòu)化數(shù)據(jù),只要這些數(shù)據(jù)是每行一個記錄。然而,如果要分析占據(jù)多行的記錄,僅僅依靠設(shè)置 FS 是不夠的。在這些情況下,我們還需要修改 RS 記錄分隔符變量。RS 變量告訴 awk 當前記錄什么時候結(jié)束,新記錄什么時候開始。
譬如,讓我們討論一下如何完成處理“聯(lián)邦證人保護計劃”所涉及人員的地址列表的任務(wù):
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345
Big Tony
200 Incognito Ave.
Suburbia, WA 67890
理論上,我們希望 awk 將每 3 行看作是一個獨立的記錄,而不是三個獨立的記錄。如果 awk 將地址的第一行看作是第一個字段 ($1),街道地址看作是第二個字段 ($2),城市、州和郵政編碼看作是第三個字段 $3,那么這個代碼就會變得很簡單。以下就是我們想要得到的代碼:
BEGIN {
FS="n"
RS=""
}
在上面這段代碼中,將 FS 設(shè)置成 "n" 告訴 awk 每個字段都占據(jù)一行。通過將 RS 設(shè)置成 "",還會告訴 awk 每個地址記錄都由空白行分隔。一旦 awk 知道是如何格式化輸入的,它就可以為我們執(zhí)行所有分析工作,腳本的其余部分很簡單。讓我們研究一個完整的腳本,它將分析這個地址列表,并將每個記錄打印在一行上,用逗號分隔每個字段。
address.awk BEGIN {
FS="n"
RS=""
}
{
print $1 ", " $2 ", " $3
}
如果這個腳本保存為 address.awk,地址數(shù)據(jù)存儲在文件 address.txt 中,可以通過輸入 "awk -f address.awk address.txt" 來執(zhí)行這個腳本。此代碼將產(chǎn)生以下輸出:
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, WA 67890
OFS 和 ORS
在 address.awk 的 print 語句中,可以看到 awk 會連接(合并)一行中彼此相鄰的字符串。我們使用此功能在同一行上的三個字段之間插入一個逗號和空格 (", ")。這個方法雖然有用,但比較難看。與其在字段間插入 ", " 字符串,倒不如讓通過設(shè)置一個特殊 awk 變量 OFS,讓 awk 完成這件事。請參考下面這個代碼片斷。
print "Hello", "there", "Jim!"
這行代碼中的逗號并不是實際文字字符串的一部分。事實上,它們告訴 awk "Hello"、"there" 和 "Jim!" 是單獨的字段,并且應(yīng)該在每個字符串之間打印 OFS 變量。缺省情況下,awk 產(chǎn)生以下輸出:
Hello there Jim!
這是缺省情況下的輸出結(jié)果,OFS 被設(shè)置成 " ",單個空格。不過,我們可以方便地重新定義 OFS,這樣 awk 將插入我們中意的字段分隔符。以下是原始 address.awk 程序的修訂版,它使用 OFS 來輸出那些中間的 ", " 字符串:
address.awk 的修訂版 BEGIN {
FS="n"
RS=""
OFS=", "
}
{
print $1, $2, $3
}
awk 還有一個特殊變量 ORS,全稱是“輸出記錄分隔符”。通過設(shè)置缺省為換行 ("n") 的 OFS,我們可以控制在 print 語句結(jié)尾自動打印的字符。缺省 ORS 值會使 awk 在新行中輸出每個新的 print 語句。如果想使輸出的間隔翻倍,可以將 ORS 設(shè)置成 "nn"。或者,如果想要用單個空格分隔記錄(而不換行),將 ORS 設(shè)置成 " "。
將多行轉(zhuǎn)換成用 tab 分隔的格式
假設(shè)我們編寫了一個腳本,它將地址列表轉(zhuǎn)換成每個記錄一行,且用 tab 定界的格式,以便導入電子表格。使用稍加修改的 address.awk 之后,就可以清楚地看到這個程序只適合于三行的地址。如果 awk 遇到以下地址,將丟掉第四行,并且不打印該行:
Cousin Vinnie
Vinnie's Auto Shop
300 City Alley
Sosueme, OR 76543
要處理這種情況,代碼最好考慮每個字段的記錄數(shù)量,并依次打印每個記錄。現(xiàn)在,代碼只打印地址的前三個字段。以下就是我們想要的一些代碼:
適合具有任意多字段的地址的 address.awk 版本 BEGIN {
FS="n"
RS=""
ORS=""
}
{
x=1
while ( x<NF ) {
print $x "t"
x++
}
print $NF "n"
}
首先,將字段分隔符 FS 設(shè)置成 "n",將記錄分隔符 RS 設(shè)置成 "",這樣 awk 可以象以前一樣正確分析多行地址。然后,將輸出記錄分隔符 ORS 設(shè)置成 "",它將使 print 語句在每個調(diào)用結(jié)尾不輸出新行。這意味著如果希望任何文本從新的一行開始,那么需要明確寫入 print "n"。
在主代碼塊中,創(chuàng)建了一個變量 x 來存儲正在處理的當前字段的編號。起初,它被設(shè)置成 1。然后,我們使用 while 循環(huán)(一種 awk 循環(huán)結(jié)構(gòu),等同于 C 語言中的 while 循環(huán)),對于所有記錄(最后一個記錄除外)重復打印記錄和 tab 字符。最后,打印最后一個記錄和換行;此外,由于將 ORS 設(shè)置成 "",print 將不輸出換行。程序輸出如下,這正是我們所期望的(不算漂亮,但用 tab 定界,以便于導入電子表格):
Jimmy the Weasel 100 Pleasant Drive San Francisco, CA 12345
Big Tony 200 Incognito Ave. Suburbia, WA 67890
Cousin Vinnie Vinnie's Auto Shop 300 City Alley Sosueme, OR 76543
循環(huán)結(jié)構(gòu)
我們已經(jīng)看到了 awk 的 while 循環(huán)結(jié)構(gòu),它等同于相應(yīng)的 C 語言 while 循環(huán)。awk 還有 "do...while" 循環(huán),它在代碼塊結(jié)尾處對條件求值,而不象標準 while 循環(huán)那樣在開始處求值。它類似于其它語言中的 "repeat...until" 循環(huán)。以下是一個示例:
do...while 示例 {
count=1
do {
print "I get printed at least once no matter what"
} while ( count != 1 )
}
與一般的 while 循環(huán)不同,由于在代碼塊之后對條件求值,"do...while" 循環(huán)永遠都至少執(zhí)行一次。換句話說,當?shù)谝淮斡龅狡胀?while 循環(huán)時,如果條件為假,將永遠不執(zhí)行該循環(huán)。
for 循環(huán)
awk 允許創(chuàng)建 for 循環(huán),它就象 while 循環(huán),也等同于 C 語言的 for 循環(huán):
for ( initial assignment; comparison; increment ) {
code block
}
以下是一個簡短示例:
for ( x = 1; x <= 4; x++ ) {
print "iteration",x
}
此段代碼將打印:
iteration 1
iteration 2
iteration 3
iteration 4
break 和 continue
此外,如同 C 語言一樣,awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制 awk 的循環(huán)結(jié)構(gòu)。以下是迫切需要 break 語句的代碼片斷:
while 死循環(huán) while (1) {
print "forever and ever..."
}
因為 1 永遠代表是真,這個 while 循環(huán)將永遠運行下去。以下是一個只執(zhí)行十次的循環(huán):
break 語句示例 x=1
while(1) {
print "iteration",x
if ( x == 10 ) {
break
}
x++
}
這里,break 語句用于“逃出”最深層的循環(huán)。"break" 使循環(huán)立即終止,并繼續(xù)執(zhí)行循環(huán)代碼塊后面的語句。
continue 語句補充了 break,其作用如下:
x=1
while (1) {
if ( x == 4 ) {
x++
continue
}
print "iteration",x
if ( x > 20 ) {
break
}
x++
}
這段代碼打印 "iteration 1" 到 "iteration 21","iteration 4" 除外。如果迭代等于 4,則增加 x 并調(diào)用 continue 語句,該語句立即使 awk 開始執(zhí)行下一個循環(huán)迭代,而不執(zhí)行代碼塊的其余部分。如同 break 一樣,continue 語句適合各種 awk 迭代循環(huán)。在 for 循環(huán)主體中使用時,continue 將使循環(huán)控制變量自動增加。以下是一個等價循環(huán):
for ( x=1; x<=21; x++ ) {
if ( x == 4 ) {
continue
}
print "iteration",x
}
在 while 循環(huán)中時,在調(diào)用 continue 之前沒有必要增加 x,因為 for 循環(huán)會自動增加 x。
數(shù)組
如果您知道 awk 可以使用數(shù)組,您一定會感到高興。然而,在 awk 中,數(shù)組下標通常從 1 開始,而不是 0:
myarray[1]="jim"
myarray[2]=456
awk 遇到第一個賦值語句時,它將創(chuàng)建 myarray,并將元素 myarray[1] 設(shè)置成 "jim"。執(zhí)行了第二個賦值語句后,數(shù)組就有兩個元素了。
數(shù)組迭代
定義之后,awk 有一個便利的機制來迭代數(shù)組元素,如下所示:
for ( x in myarray ) {
print myarray[x]
}
這段代碼將打印數(shù)組 myarray 中的每一個元素。當對于 for 使用這種特殊的 "in" 形式時,awk 將 myarray 的每個現(xiàn)有下標依次賦值給 x(循環(huán)控制變量),每次賦值以后都循環(huán)一次循環(huán)代碼。雖然這是一個非常方便的 awk 功能,但它有一個缺點 -- 當 awk 在數(shù)組下標之間輪轉(zhuǎn)時,它不會依照任何特定的順序。那就意味著我們不能知道以上代碼的輸出是:
jim
456
還是:
456
jim
套用 Forrest Gump 的話來說,迭代數(shù)組內(nèi)容就像一盒巧克力 -- 您永遠不知道將會得到什么。因此有必要使 awk 數(shù)組“字符串化”,我們現(xiàn)在就來研究這個問題。
數(shù)組下標字符串化
在我的前一篇文章中,我演示了 awk 實際上以字符串格式來存儲數(shù)字值。雖然 awk 要執(zhí)行必要的轉(zhuǎn)換來完成這項工作,但它卻可以使用某些看起來很奇怪的代碼:
a="1"
b="2"
c=a+b+3
執(zhí)行了這段代碼后,c 等于 6。由于 awk 是“字符串化”的,添加字符串 "1" 和 "2" 在功能上并不比添加數(shù)字 1 和 2 難。這兩種情況下,awk 都可以成功執(zhí)行運算。awk 的“字符串化”性質(zhì)非??蓯?-- 您可能想要知道如果使用數(shù)組的字符串下標會發(fā)生什么情況。例如,使用以下代碼:
myarr["1"]="Mr. Whipple"
print myarr["1"]
可以預(yù)料,這段代碼將打印 "Mr. Whipple"。但如果去掉第二個 "1" 下標中的引號,情況又會怎樣呢?
myarr["1"]="Mr. Whipple"
print myarr[1]
猜想這個代碼片斷的結(jié)果比較難。awk 將 myarr["1"] 和 myarr[1] 看作數(shù)組的兩個獨立元素,還是它們是指同一個元素?答案是它們指的是同一個元素,awk 將打印 "Mr. Whipple",如同第一個代碼片斷一樣。雖然看上去可能有點怪,但 awk 在幕后卻一直使用數(shù)組的字符串下標!
了解了這個奇怪的真相之后,我們中的一些人可能想要執(zhí)行類似于以下的古怪代碼:
myarr["name"]="Mr. Whipple"
print myarr["name"]
這段代碼不僅不會產(chǎn)生錯誤,而且它的功能與前面的示例完全相同,也將打印 "Mr. Whipple"!可以看到,awk 并沒有限制我們使用純整數(shù)下標;如果我們愿意,可以使用字符串下標,而且不會產(chǎn)生任何問題。只要我們使用非整數(shù)數(shù)組下標,如 myarr["name"],那么我們就在使用關(guān)聯(lián)數(shù)組。從技術(shù)上講,如果我們使用字符串下標,awk 的后臺操作并沒有什么不同(因為即便使用“整數(shù)”下標,awk 還是會將它看作是字符串)。但是,應(yīng)該將它們稱作關(guān)聯(lián)數(shù)組 -- 它聽起來很酷,而且會給您的上司留下印象。字符串化下標是我們的小秘密。;)
數(shù)組工具
談到數(shù)組時,awk 給予我們許多靈活性??梢允褂米址聵耍也恍枰B續(xù)的數(shù)字序列下標(例如,可以定義 myarr[1] 和 myarr[1000],但不定義其它所有元素)。雖然這些都很有用,但在某些情況下,會產(chǎn)生混淆。幸好,awk 提供了一些實用功能有助于使數(shù)組變得更易于管理。
首先,可以刪除數(shù)組元素。如果想要刪除數(shù)組 fooarray 的元素 1,輸入:
delete fooarray[1]
而且,如果想要查看是否存在某個特定數(shù)組元素,可以使用特殊的 "in" 布爾運算符,如下所示:
if ( 1 in fooarray ) {
print "Ayep! It's there."
} else {
print "Nope! Can't find it."
}
格式化輸出
雖然大多數(shù)情況下 awk 的 print 語句可以完成任務(wù),但有時我們還需要更多。在那些情況下,awk 提供了兩個我們熟知的老朋友 printf() 和 sprintf()。是的,如同其它許多 awk 部件一樣,這些函數(shù)等同于相應(yīng)的 C 語言函數(shù)。printf() 會將格式化字符串打印到 stdout,而 sprintf() 則返回可以賦值給變量的格式化字符串。如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速了解這兩個基本打印函數(shù)。在 Linux 系統(tǒng)上,可以輸入 "man 3 printf" 來查看 printf() 幫助頁面。
以下是一些 awk sprintf() 和 printf() 的樣本代碼??梢钥吹?,它們幾乎與 C 語言完全相同。
x=1
b="foo"
printf("%s got a %d on the last testn","Jim",83)
myout=("%s-%d",b,x)
print myout
此代碼將打印:
Jim got a 83 on the last test
foo-1
字符串函數(shù)
awk 有許多字符串函數(shù),這是件好事。在 awk 中,確實需要字符串函數(shù),因為不能象在其它語言(如 C、C++ 和 Python)中那樣將字符串看作是字符數(shù)組。例如,如果執(zhí)行以下代碼:
mystring="How are you doing today?"
print mystring[3]
將會接收到一個錯誤,如下所示:
awk: string.gawk:59: fatal: attempt to use scalar as array
噢,好吧。雖然不象 Python 的序列類型那樣方便,但 awk 的字符串函數(shù)還是可以完成任務(wù)。讓我們來看一下。
首先,有一個基本 length() 函數(shù),它返回字符串的長度。以下是它的使用方法:
print length(mystring)
此代碼將打印值:
24
好,繼續(xù)。下一個字符串函數(shù)叫作 index,它將返回子字符串在另一個字符串中出現(xiàn)的位置,如果沒有找到該字符串則返回 0。使用 mystring,可以按以下方法調(diào)用它:
print index(mystring,"you")
awk 會打印:
9
讓我們繼續(xù)討論另外兩個簡單的函數(shù),tolower() 和 toupper()。與您猜想的一樣,這兩個函數(shù)將返回字符串并且將所有字符分別轉(zhuǎn)換成小寫或大寫。請注意,tolower() 和 toupper() 返回新的字符串,不會修改原來的字符串。這段代碼:
print tolower(mystring)
print toupper(mystring)
print mystring
……將產(chǎn)生以下輸出:
how are you doing today?
HOW ARE YOU DOING TODAY?
How are you doing today?
到現(xiàn)在為止一切不錯,但我們究竟如何從字符串中選擇子串,甚至單個字符?那就是使用 substr() 的原因。以下是 substr() 的調(diào)用方法:
mysub=substr(mystring,startpos,maxlen)
mystring 應(yīng)該是要從中抽取子串的字符串變量或文字字符串。startpos 應(yīng)該設(shè)置成起始字符位置,maxlen 應(yīng)該包含要抽取的字符串的最大長度。請注意,我說的是最大長度;如果 length(mystring) 比 startpos+maxlen 短,那么得到的結(jié)果就會被截斷。substr() 不會修改原始字符串,而是返回子串。以下是一個示例:
print substr(mystring,9,3)
awk 將打?。?br />
you
如果您通常用于編程的語言使用數(shù)組下標訪問部分字符串(以及不使用這種語言的人),請記住 substr() 是 awk 代替方法。需要使用它來抽取單個字符和子串;因為 awk 是基于字符串的語言,所以會經(jīng)常用到它。
一些更耐人尋味的函數(shù)
首先是 match()。match() 與 index() 非常相似,它與 index() 的區(qū)別在于它并不搜索子串,它搜索的是規(guī)則表達式。match() 函數(shù)將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match() 還將設(shè)置兩個變量,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH 指定它占據(jù)的字符跨度(如果沒有找到匹配,則返回 -1)。通過使用 RSTART、RLENGTH、substr() 和一個小循環(huán),可以輕松地迭代字符串中的每個匹配。以下是一個 match() 調(diào)用示例:
print match(mystring,/you/), RSTART, RLENGTH
awk 將打?。?br />
9 9 3
字符串替換
現(xiàn)在,我們將研究兩個字符串替換函數(shù),sub() 和 gsub()。這些函數(shù)與目前已經(jīng)討論過的函數(shù)略有不同,因為它們確實修改原始字符串。以下是一個模板,顯示了如何調(diào)用 sub():
sub(regexp,replstring,mystring)
調(diào)用 sub() 時,它將在 mystring 中匹配 regexp 的第一個字符序列,并且用 replstring 替換該序列。sub() 和 gsub() 用相同的自變量;唯一的區(qū)別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub() 將執(zhí)行全局替換,換出字符串中的所有匹配。以下是一個 sub() 和 gsub() 調(diào)用示例:
sub(/o/,"O",mystring)
print mystring
mystring="How are you doing today?"
gsub(/o/,"O",mystring)
print mystring
必須將 mystring 復位成其初始值,因為第一個 sub() 調(diào)用直接修改了 mystring。在執(zhí)行時,此代碼將使 awk 輸出:
HOw are you doing today?
HOw are yOu dOing tOday?
當然,也可以是更復雜的規(guī)則表達式。我把測試一些復雜規(guī)則表達式的任務(wù)留給您來完成。
通過介紹函數(shù) split(),我們來匯總一下已討論過的函數(shù)。split() 的任務(wù)是“切開”字符串,并將各部分放到使用整數(shù)下標的數(shù)組中。以下是一個 split() 調(diào)用示例:
numelements=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",mymonths,",")
調(diào)用 split() 時,第一個自變量包含要切開文字字符串或字符串變量。在第二個自變量中,應(yīng)該指定 split() 將填入片段部分的數(shù)組名稱。在第三個元素中,指定用于切開字符串的分隔符。split() 返回時,它將返回分割的字符串元素的數(shù)量。split() 將每一個片段賦值給下標從 1 開始的數(shù)組,因此以下代碼:
print mymonths[1],mymonths[numelements]
……將打印:
Jan Dec
特殊字符串形式
簡短注釋 -- 調(diào)用 length()、sub() 或 gsub() 時,可以去掉最后一個自變量,這樣 awk 將對 $0(整個當前行)應(yīng)用函數(shù)調(diào)用。要打印文件中每一行的長度,使用以下 awk 腳本:
{
print length()
}
sed使用規(guī)則
sed 是很有用(但常被遺忘)的 UNIX 流編輯器。sed是十分強大和小巧的文本流編輯器。使用sed 可以執(zhí)行字符串替換、創(chuàng)建更大的 sed 腳本以及使用 sed 的附加、插入和更改行命令。在以批處理方式編輯文件或以有效方式創(chuàng)建 shell 腳本來修改現(xiàn)有文件方面,它是十分理想的工具。
sed 示例
sed 通過對輸入數(shù)據(jù)執(zhí)行任意數(shù)量用戶指定的編輯操作(“命令”)來工作。sed 是基于行的,因此按順序?qū)γ恳恍袌?zhí)行命令。然后,sed 將其結(jié)果寫入標準輸出 (stdout),它不修改任何輸入文件。
讓我們看一些示例。頭幾個會有些奇怪,因為我要用它們演示 sed 如何工作,而不是執(zhí)行任何有用的任務(wù)。然而,如果您是 sed 新手,那么理解它們是十分重要的。下面是第一個示例:
$ sed -e 'd' /etc/services
如果輸入該命令,將得不到任何輸出。那么,發(fā)生了什么?在該例中,用一個編輯命令 'd' 調(diào)用 sed。sed 打開 /etc/services 文件,將一行讀入其模式緩沖區(qū),執(zhí)行編輯命令(“刪除行”),然后打印模式緩沖區(qū)(緩沖區(qū)已為空)。然后,它對后面的每一行重復這些步驟。這不會產(chǎn)生輸出,因為 "d" 命令除去了模式緩沖區(qū)中的每一行!
在該例中,還有幾件事要注意。首先,根本沒有修改 /etc/services。這還是因為 sed 只讀取在命令行指定的文件,將其用作輸入 -- 它不試圖修改該文件。第二件要注意的事是 sed 是面向行的。'd' 命令不是簡單地告訴 sed 一下子刪除所有輸入數(shù)據(jù)。相反,sed 逐行將 /etc/services 的每一行讀入其稱為模式緩沖區(qū)的內(nèi)部緩沖區(qū)。一旦將一行讀入模式緩沖區(qū),它就執(zhí)行 'd' 命令,然后打印模式緩沖區(qū)的內(nèi)容(在本例中沒有內(nèi)容)。我將在后面為您演示如何使用地址范圍來控制將命令應(yīng)用到哪些行 -- 但是,如果不使用地址,命令將應(yīng)用到所有行。第三件要注意的事是括起 'd' 命令的單引號的用法。養(yǎng)成使用單引號來括起 sed 命令的習慣是個好注意,這樣可以禁用 shell 擴展。
另一個 sed 示例
下面是使用 sed 從輸出流除去 /etc/services 文件第一行的示例:
$ sed -e '1d' /etc/services | more
如您所見,除了前面有 '1' 之外,該命令與第一個 'd' 命令十分類似。如果您猜到 '1' 指的是第一行,那您就猜對了。與第一個示例中只使用 'd' 不同的是,這一次使用的 'd' 前面有一個可選的數(shù)字地址。通過使用地址,可以告訴 sed 只對某一或某些特定行進行編輯。
地址范圍
現(xiàn)在,讓我們看一下如何指定地址范圍。在本例中,sed 將刪除輸出的第 1 到 10 行:
$ sed -e '1,10d' /etc/services | more
當用逗號將兩個地址分開時,sed 將把后面的命令應(yīng)用到從第一個地址開始、到第二個地址結(jié)束的范圍。在本例中,將 'd' 命令應(yīng)用到第 1 到 10 行(包括這兩行)。所有其它行都被忽略。
帶規(guī)則表達式的地址
現(xiàn)在演示一個更有用的示例。假設(shè)要查看 /etc/services 文件的內(nèi)容,但是對查看其中包括的注釋部分不感興趣。如您所知,可以通過以 '#' 字符開頭的行在 /etc/services 文件中放置注釋。為了避免注釋,我們希望 sed 刪除以 '#' 開始的行。以下是具體做法:
$ sed -e '/^#/d' /etc/services | more
試一下該例,看看發(fā)生了什么。您將注意到,sed 成功完成了預(yù)期任務(wù)。現(xiàn)在,讓我們分析發(fā)生的情況:
要理解 '/^#/d' 命令,首先需要對其剖析。首先,讓我們除去 'd' -- 這是我們前面所使用的同一個刪除行命令。新增加的是 '/^#/' 部分,它是一種新的規(guī)則表達式地址。規(guī)則表達式地址總是由斜杠括起。它們指定一種 模式,緊跟在規(guī)則表達式地址之后的命令將僅適用于正好與該特定模式匹配的行。因此,'/^#/' 是一個規(guī)則表達式。(規(guī)則表達式的有關(guān)規(guī)定可以參見本文前面的內(nèi)容)
例如:
$ sed -e '/regexp/d' /path/to/my/test/file | more
這將導致 sed 刪除任何匹配的行。
對比如下的命令:
$ sed -n -e '/regexp/p' /path/to/my/test/file | more
請注意新的 '-n' 選項,該選項告訴 sed 除非明確要求打印模式空間,否則不這樣做。您還會注意到,我們用 'p' 命令替換了 'd' 命令,如您所猜想的那樣,這明確要求 sed 打印模式空間。就這樣,將只打印匹配部分。
有關(guān)地址的更多內(nèi)容
目前為止,我們已經(jīng)看到了行地址、行范圍地址和 regexp 地址。但是,還有更多的可能。我們可以指定兩個用逗號分開的規(guī)則表達式,sed 將與所有從匹配第一個規(guī)則表達式的第一行開始,到匹配第二個規(guī)則表達式的行結(jié)束(包括該行)的所有行匹配。
例如,以下命令將打印從包含 "BEGIN" 的行開始,并且以包含 "END" 的行結(jié)束的文本塊:
$ sed -n -e '/BEGIN/,/END/p' /my/test/file | more
如果沒發(fā)現(xiàn) "BEGIN",那么將不打印數(shù)據(jù)。如果發(fā)現(xiàn)了 "BEGIN",但是在這之后的所有行中都沒發(fā)現(xiàn) "END",那么將打印所有后續(xù)行。發(fā)生這種情況是因為 sed 面向流的特性 -- 它不知道是否會出現(xiàn) "END"。
C 源代碼示例
如果只要打印 C 源文件中的 main() 函數(shù),可輸入:
$ sed -n -e '/main[[:space:]]*(/,/^)/p' sourcefile.c | more
該命令有兩個規(guī)則表達式 '/main[[:space:]]*(/' 和 '/^}/',以及一個命令 'p'。第一個規(guī)則表達式將與后面依次跟有任意數(shù)量的空格或制表鍵以及開始圓括號的字符串 "main" 匹配。這應(yīng)該與一般 ANSI C main() 聲明的開始匹配。
在這個特別的規(guī)則表達式中,出現(xiàn)了 '[[:space:]]' 字符類。這只是一個特殊的關(guān)鍵字,它告訴 sed 與 TAB 或空格匹配。如果愿意的話,可以不輸入 '[[:space:]]',而輸入 '[',然后是空格字母,然后是 -V,然后再輸入制表鍵字母和 ']' -- Control-V 告訴 bash 要插入“真正”的制表鍵,而不是執(zhí)行命令擴展。使用 '[[:space:]]' 命令類(特別是在腳本中)會更清楚。
現(xiàn)在看一下第二個 regexp。'/^}' 將與任何出現(xiàn)在新行行首的 '}' 字符匹配。如果代碼的格式很好,那么這將與 main() 函數(shù)的結(jié)束花括號匹配。如果格式不好,則不會正確匹配 -- 這是執(zhí)行模式匹配任務(wù)的一件棘手之事。因為是處于 '-n' 安靜方式,所以 'p' 命令還是完成其慣有任務(wù),即明確告訴 sed 打印該行。試著對 C 源文件運行該命令 -- 它應(yīng)該輸出整個 main() { } 塊,包括開始的 "main()" 和結(jié)束的 '}'。
替換
讓我們看一下 sed 最有用的命令之一,替換命令。使用該命令,可以將特定字符串或匹配的規(guī)則表達式用另一個字符串替換。
下面是該命令最基本用法的示例:
$ sed -e 's/foo/bar/' myfile.txt
上面的命令將 myfile.txt 中每行第一次出現(xiàn)的 'foo'(如果有的話)用字符串 'bar' 替換,然后將該文件內(nèi)容輸出到標準輸出。請注意,我說的是每行第一次出現(xiàn),盡管這通常不是您想要的。在進行字符串替換時,通常想執(zhí)行全局替換。也就是說,要替換每行中的所有出現(xiàn),如下所示:
$ sed -e 's/foo/bar/g' myfile.txt
在最后一個斜杠之后附加的 'g' 選項告訴 sed 執(zhí)行全局替換。
關(guān)于 's///' 替換命令,還有其它幾件要了解的事。首先,它是一個命令,并且只是一個命令,在所有上例中都沒有指定地址。這意味著,'s///' 還可以與地址一起使用來控制要將命令應(yīng)用到哪些行,如下所示:
$ sed -e '1,10s/enchantment/entrapment/g' myfile2.txt
上例將導致用短語 'entrapment' 替換所有出現(xiàn)的短語 'enchantment',但是只在第一到第十行(包括這兩行)上這樣做。
$ sed -e '/^$/,/^END/s/hills/mountains/g' myfile3.txt
該例將用 'mountains' 替換 'hills',但是,只從空行開始,到以三個字符 'END' 開始的行結(jié)束(包括這兩行)的文本塊上這樣做。
關(guān)于 's///' 命令的另一個妙處是 '/' 分隔符有許多替換選項。如果正在執(zhí)行字符串替換,并且規(guī)則表達式或替換字符串中有許多斜杠,則可以通過在 's' 之后指定一個不同的字符來更改分隔符。例如,下例將把所有出現(xiàn)的 /usr/local 替換成 /usr:
$ sed -e 's:/usr/local:/usr:g' mylist.txt
在該例中,使用冒號作為分隔符。如果需要在規(guī)則表達式中指定分隔符字符,可以在它前面加入反斜杠。
規(guī)則表達式混亂
目前為止,我們只執(zhí)行了簡單的字符串替換。雖然這很方便,但是我們還可以匹配規(guī)則表達式。例如,以下 sed 命令將匹配從 '<' 開始、到 '>' 結(jié)束、并且在其中包含任意數(shù)量字符的短語。下例將刪除該短語(用空字符串替換):
$ sed -e 's/<.*>//g' myfile.html
這是要從文件除去 HTML 標記的第一個很好的 sed 腳本嘗試,但是由于規(guī)則表達式的特有規(guī)則,它不會很好地工作。原因何在?當 sed 試圖在行中匹配規(guī)則表達式時,它要在行中查找最長的匹配。在我的前一篇 sed 文章中,這不成問題,因為我們使用的是 'd' 和 'p' 命令,這些命令總要刪除或打印整行。但是,在使用 's///' 命令時,確實有很大不同,因為規(guī)則表達式匹配的整個部分將被目標字符串替換,或者,在本例中,被刪除。這意味著,上例將把下行:
<b>This</b> is what <b>I</b> meant.
變成:meant.
我們要的不是這個,而是:This is what I meant.
幸運的是,有一種簡便方法來糾正該問題。我們不輸入“'<' 字符后面跟有一些字符并以 '>' 字符結(jié)束”的規(guī)則表達式,而只需輸入一個“'<' 字符后面跟有任意數(shù)量非 '>' 字符并以 '>' 字符結(jié)束”的規(guī)則表達式。這將與最短、而不是最長的可能性匹配。新命令如下:
$ sed -e 's/<[^>]*>//g' myfile.html
在上例中,'[^>]' 指定“非 '>'”字符,其后的 '*' 完成該表達式以表示“零或多個非 '>' 字符”。對幾個 html 文件測試該命令,將它們管道輸出到 "more",然后仔細查看其結(jié)果。
更多字符匹配
'[ ]' 規(guī)則表達式語法還有一些附加選項。要指定字符范圍,只要字符不在第一個或最后一個位置,就可以使用 '-',如下所示:
'[a-x]*'
這將匹配零或多個全部為 'a'、'b'、'c'...'v'、'w'、'x' 的字符。另外,可以使用 '[:space:]' 字符類來匹配空格(字符類的相關(guān)信息可以參見本文前面部分內(nèi)容)。
高級替換功能
我們已經(jīng)看到如何執(zhí)行簡單甚至有些復雜的直接替換,但是 sed 還可以做更多的事。實際上可以引用匹配規(guī)則表達式的部分或全部,并使用這些部分來構(gòu)造替換字符串。作為示例,假設(shè)您正在回復一條消息。下例將在每一行前面加上短語 "ralph said: ":
$ sed -e 's/.*/ralph said: &/' origmsg.txt
輸出如下:
ralph said: Hiya Jim, ralph said: ralph said:
I sure like this sed stuff! ralph said:
該例的替換字符串中使用了 '&' 字符,該字符告訴 sed 插入整個匹配的規(guī)則表達式。因此,可以將與 '.*' 匹配的任何內(nèi)容(行中的零或多個字符的最大組或整行)插入到替換字符串中的任何位置,甚至多次插入。這非常好,但 sed 甚至更強大。
那些極好的帶反斜杠的圓括號
's///' 命令甚至比 '&' 更好,它允許我們在規(guī)則表達式中定義區(qū)域,然后可以在替換字符串中引用這些特定區(qū)域。作為示例,假設(shè)有一個包含以下文本的文件:
bar oni eeny meeny miny larry curly moe jimmy the weasel
現(xiàn)在假設(shè)要編寫一個 sed 腳本,該腳本將把 "eeny meeny miny" 替換成 "Victor eeny-meeny Von miny" 等等。要這樣做,首先要編寫一個由空格分隔并與三個字符串匹配的規(guī)則表達式:
'.* .* .*'
現(xiàn)在,將在其中每個感興趣的區(qū)域兩邊插入帶反斜杠的圓括號來定義區(qū)域:
'(.*) (.*) (.*)'
除了要定義三個可在替換字符串中引用的邏輯區(qū)域以外,該規(guī)則表達式的工作原理將與第一個規(guī)則表達式相同。下面是最終腳本:
$ sed -e 's/(.*) (.*) (.*)/Victor 1-2 Von 3/' myfile.txt
如您所見,通過輸入 'x'(其中,x 是從 1 開始的區(qū)域號)來引用每個由圓括號定界的區(qū)域。輸入如下:
Victor foo-bar Von oni Victor eeny-meeny Von miny Victor larry-curly Von moe Victor jimmy-the Von weasel
隨著對 sed 越來越熟悉,您可以花最小力氣來進行相當強大的文本處理。您可能想如何使用熟悉的腳本語言來處理這種問題 -- 能用一行代碼輕易實現(xiàn)這樣的解決方案嗎?
組合使用
在開始創(chuàng)建更復雜的 sed 腳本時,需要有輸入多個命令的能力。有幾種方法這樣做。首先,可以在命令之間使用分號。例如,以下命令系列使用 '=' 命令和 'p' 命令,'=' 命令告訴 sed 打印行號,'p' 命令明確告訴 sed 打印該行(因為處于 '-n' 模式)。
$ sed -n -e '=;p' myfile.txt
無論什么時候指定了兩個或更多命令,都按順序?qū)⒚總€命令應(yīng)用到文件的每一行。在上例中,首先將 '=' 命令應(yīng)用到第 1 行,然后應(yīng)用 'p' 命令。接著,sed 繼續(xù)處理第 2 行,并重復該過程。雖然分號很方便,但是在某些場合下,它不能正常工作。另一種替換方法是使用兩個 -e 選項來指定兩個不同的命令:
$ sed -n -e '=' -e 'p' myfile.txt
然而,在使用更為復雜的附加和插入命令時,甚至多個 '-e' 選項也不能幫我們的忙。對于復雜的多行腳本,最好的方法是將命令放入一個單獨的文件中。然后,用 -f 選項引用該腳本文件:
$ sed -n -f mycommands.sed myfile.txt
這種方法雖然可能不太方便,但總是管用。
一個地址的多個命令
有時,可能要指定應(yīng)用到一個地址的多個命令。這在執(zhí)行許多 's///' 以變換源文件中的字和語法時特別方便。要對一個地址執(zhí)行多個命令,可在文件中輸入 sed 命令,然后使用 '{ }' 字符將這些命令分組,如下所示:
1,20{ s/[Ll]inux/GNU/Linux/g s/samba/Samba/g s/posix/POSIX/g }
上例將把三個替換命令應(yīng)用到第 1 行到第 20 行(包括這兩行)。還可以使用規(guī)則表達式地址或者二者的組合:
1,/^END/{ s/[Ll]inux/GNU/Linux/g s/samba/Samba/g s/posix/POSIX/g p }
該例將把 '{ }' 之間的所有命令應(yīng)用到從第 1 行開始,到以字母 "END" 開始的行結(jié)束(如果在源文件中沒發(fā)現(xiàn) "END",則到文件結(jié)束)的所有行。
附加、插入和更改行
既然在單獨的文件中編寫 sed 腳本,我們可以利用附加、插入和更改行命令。這些命令將在當前行之后插入一行,在當前行之前插入一行,或者替換模式空間中的當前行。它們也可以用來將多行插入到輸出。插入行命令用法如下:
i This line will be inserted before each line
如果不為該命令指定地址,那么它將應(yīng)用到每一行,并產(chǎn)生如下的輸出:
This line will be inserted before each line line 1 here
This line will be inserted before each line line 2 here
This line will be inserted before each line line 3 here
This line will be inserted before each line line 4 here
如果要在當前行之前插入多行,可以通過在前一行之后附加一個反斜杠來添加附加行,如下所示:
i insert this line and this one and this one and, uh, this one too.
附加命令的用法與之類似,但是它將把一行或多行插入到模式空間中的當前行之后。其用法如下:
a insert this line after each line. Thanks! :)
另一方面,“更改行”命令將實際替換模式空間中的當前行,其用法如下:
c You're history, original line! Muhahaha!
因為附加、插入和更改行命令需要在多行輸入,所以將把它們輸入到一個文本 sed 腳本中,然后通過使用 '-f' 選項告訴 sed 執(zhí)行它們。使用其它方法將命令傳遞給 sed 會出現(xiàn)問題。
使用 sed 的幾個示例
這些示例不僅演示 sed 的能力,而且還做一些真正巧妙(和方便)的事。例如,在本文的后半部,將為您演示如何設(shè)計一個 sed 腳本來將 .QIF 文件從 Intuit 的 Quicken 金融程序轉(zhuǎn)換成具有良好格式的文本文件。在那樣做之前,我們將看一下不怎么復雜但卻很有用的 sed 腳本。
l 文本轉(zhuǎn)換
第一個實際腳本將 UNIX 風格的文本轉(zhuǎn)換成 DOS/Windows 格式。您可能知道,基于 DOS/Windows 的文本文件在每一行末尾有一個 CR(回車)和 LF(換行),而 UNIX 文本只有一個換行。有時可能需要將某些 UNIX 文本移至 Windows 系統(tǒng),該腳本將為您執(zhí)行必需的格式轉(zhuǎn)換。
$ sed -e 's/$/r/' myunix.txt > mydos.txt
在該腳本中,'$' 規(guī)則表達式將與行的末尾匹配,而 'r' 告訴 sed 在其之前插入一個回車。在換行之前插入回車,立即,每一行就以 CR/LF 結(jié)束。請注意,僅當使用 GNU sed 3.02.80 或以后的版本時,才會用 CR 替換 'r'。如果還沒有安裝 GNU sed 3.02.80,請在我的第一篇 sed 文章中查看如何這樣做的說明。
我已記不清有多少次在下載一些示例腳本或 C 代碼之后,卻發(fā)現(xiàn)它是 DOS/Windows 格式。雖然很多程序不在乎 DOS/Windows 格式的 CR/LF 文本文件,但是有幾個程序卻在乎 -- 最著名的是 bash,只要一遇到回車,它就會出問題。以下 sed 調(diào)用將把 DOS/Windows 格式的文本轉(zhuǎn)換成可信賴的 UNIX 格式:
$ sed -e 's/.$//' mydos.txt > myunix.txt
該腳本的工作原理很簡單:替代規(guī)則表達式與一行的最末字符匹配,而該字符恰好就是回車。我們用空字符替換它,從而將其從輸出中徹底刪除。如果使用該腳本并注意到已經(jīng)刪除了輸出中每行的最末字符,那么,您就指定了已經(jīng)是 UNIX 格式的文本文件。也就沒必要那樣做了!
l 反轉(zhuǎn)行
下面是另一個方便的小腳本。與大多數(shù) Linux 發(fā)行版中包括的 "tac" 命令一樣,該腳本將反轉(zhuǎn)文件中行的次序。"tac" 這個名稱可能會給人以誤導,因為 "tac" 不反轉(zhuǎn)行中字符的位置(左和右),而是反轉(zhuǎn)文件中行的位置(上和下)。用 "tac" 處理以下文件:
foo bar oni
....將產(chǎn)生以下輸出:
oni bar foo
可以用以下 sed 腳本達到相同目的:
$ sed -e '1!G;h;$!d' forward.txt > backward.txt
如果登錄到恰巧沒有 "tac" 命令的 FreeBSD 系統(tǒng),將發(fā)現(xiàn)該 sed 腳本很有用。雖然方便,但最好還是知道該腳本為什么那樣做。讓我們對它進行討論。
反轉(zhuǎn)解釋:首先,該腳本包含三個由分號隔開的單獨 sed 命令:'1!G'、'h' 和 '$!d'?,F(xiàn)在,需要好好理解用于第一個和第三個命令的地址。如果第一個命令是 '1G',則 'G' 命令將只應(yīng)用第一行。然而,還有一個 '!' 字符 -- 該 '!' 字符忽略該地址,即,'G' 命令將應(yīng)用到除第一行之外的所有行。'$!d' 命令與之類似。如果命令是 '$d',則將只把 'd' 命令應(yīng)用到文件中的最后一行('$' 地址是指定最后一行的簡單方式)。然而,有了 '!' 之后,'$!d' 將把 'd' 命令應(yīng)用到除最后一行之外的所有行?,F(xiàn)在,我們所要理解的是這些命令本身做什么。
當對上面的文本文件執(zhí)行反轉(zhuǎn)腳本時,首先執(zhí)行的命令是 'h'。該命令告訴 sed 將模式空間(保存正在處理的當前行的緩沖區(qū))的內(nèi)容復制到保留空間(臨時緩沖區(qū))。然后,執(zhí)行 'd' 命令,該命令從模式空間中刪除 "foo",以便在對這一行執(zhí)行完所有命令之后不打印它。
現(xiàn)在,第二行。在將 "bar" 讀入模式空間之后,執(zhí)行 'G' 命令,該命令將保留空間的內(nèi)容 ("foon") 附加到模式空間 ("barn"),使模式空間的內(nèi)容為 "barnfoon"。'h' 命令將該內(nèi)容放回保留空間保護起來,然后,'d' 從模式空間刪除該行,以便不打印它。
對于最后的 "oni" 行,除了不刪除模式空間的內(nèi)容(由于 'd' 之前的 '$!')以及將模式空間的內(nèi)容(三行)打印到標準輸出之外,重復同樣的步驟。
linux常用腳本和函數(shù)
l #查找當前目錄中是否存在指定目錄,若不存在,則創(chuàng)建之
function mkdir_1
{
if test ! -d $1
then
mkdir $1
fi
}
l #將指定文件中的"prefix = .*"串替換為"prefix=/home/gnome-unicore-install2/usr/"
#可以用來作為sed用法的參考
function modify_prefix
{
chmod +w $1
cp $1 $1.bak
sed 's/prefix = .*/prefix=/home/gnome-unicore-install2/usr/g' $1.bak > $1
rm $1.bak
}
l #將指定文件中的"^LDFLAGS =.*"串替換為"LDFLAGS = -rdynamic -lgdk_pixbuf -lgtk -lgdk -lgmodule -lglib -ldl -lXext -lX11 -lm"
#change_gnome-config FILENAME
function change_gnome-config
{
cp $1 $1.bak
sed 's/^LDFLAGS =.*/LDFLAGS = -rdynamic -lgdk_pixbuf -lgtk -lgdk -lgmodule -lglib -ldl -lXext -lX11 -lm /g' $1.bak> $1
rm $1.bak
}
l #刪除指定文件的含有指定字符的行
#格式:delete_line filename "word_contain"
function delete_line
{
chmod +w $1
cp $1 $1.bak
cat $1.bak | grep -v -e "$2" >$1
}
l #用途:刪除文件中包含line1或(和?)line2的行
#格式:delete_line filename line1 line2
function delete_line_no
{
chmod +w $1
cp $1 $1.bak
sed $2,$3'd' $1.bak>$1
rm $1.bak
}
l #用途:在LINE_NO指定的行插入字符串CONTENT
#可以用來作為sed用法的參考
#格式: add_line FILENAME LINE_NO CONTENT
function add_line
{
chmod +w $1
cp $1 $1.bak
sed -e $2 'i' "$3" '' $1.bak > $1
rm $1.bak
}
l #用途:檢查含有"PC24"代碼的程序并打印出來
#格式: check_PC24 //after installation
function check_PC24
{
echo "now comes the PC24 checking..."
. $COMMAND_UNICORE/shell/shell_PC24 >& /dev/null
if test -s $COMMAND_UNICORE/PC24_result
then :
echo "The following file contains PC24 problems: $COMMAND_UNICORE/PC24_result "
else
echo "No PC24 problem found"
fi
}
l #打印標題
displayheader() {
echo " *****************************************"
echo " * IeeeCC754 testing tool *"
echo " *****************************************"
echo " "
}
l #打印一個菜單的做法
displayplatformmenu() {
#clear the screen
clear
displayheader
echo " a) SunSparc "
echo " b) IntelPentium "
echo " c) AMD "
echo " d) Unicore32 "
echo " e) Unicore32(with FP2001) "
echo " "
echo -n " select a Platform > "
}
l #接收一個菜單輸入
displayplatformmenu
read answer
case ${answer} in
a) TARGET="BasicOp";;
b) TARGET="Conversion";;
*) badchoice;;
esac
l #查找當前目錄下是否存在file_name文件
#可以用來作為if用法的參考
detectfile_name() {
if [ ! -f file_name ]
then
echo "Error: file_name does not exist. Please check"
exit 1;
else
echo "OK,the directy is exist"
fi
}
l #將參數(shù)指定的一個或多個目錄項以及其下的多級子目錄下的所有文件名和目錄名轉(zhuǎn)換為小寫。
cvitem()
{
echo "mv $1 `dirname $1`/`basename $1 | tr
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`"
}
[ $# = 0 ] && { echo "Usage: lcdir item1 item2 ..."; exit; }
for item in $* #可以用來作為for用法的參考
do
[ "`dirname $item`" != "`basename $item`" ] && {
[ -d $item ] &&
{
for subitem in `ls $item`
do
cvlc $item/$subitem
done
}
cvitem $item
}
done
l #一個login的例子
if ($?path) then
set path=($HOME/bin $path)
else
set path=($HOME/bin /usr/bin .)
endif
if ( ! $ {?DT} ); then
stty dec new
tset -I -Q
endif
set mail=/usr/spool/mail/$USER
l #關(guān)于if使用的幾個例子
n #執(zhí)行一個命令或程序之前,先檢查該命令是否存在,然後才執(zhí)行
if [ -x /sbin/quotaon ] ; then
echo "Turning on Quota for root filesystem"
/sbin/quotaon /
fi
n #得到Hostname
#!/bin/sh
if [ -f /etc/HOSTNAME ] ; then
HOSTNAME=`cat /etc/HOSTNAME`
else
HOSTNAME=localhost
fi
n #如果某個設(shè)定檔允許有好幾個位置的話,例如crontab,可利用if then elif fi來找尋
if [ -f /etc/crontab ] ; then #[ -f /etc/crontab ]等價于test -f /etc/crontab
CRONTAB="/etc/crontab"
elif [ -f /var/spool/cron/crontabs/root ] ; then
CRONTAB="/var/spool/cron/crontabs/root"
elif [ -f /var/cron/tabs/root ] ; then
CRONTAB="/var/cron/tabs/root"
fi
export CRONTAB

n #利用uname來判斷目前系統(tǒng),并分別做各系統(tǒng)狀況不同的事。
SYSTEM=`uname -s`
if [ $SYSTEM = "Linux" ] ; then
echo "Linux"
elif [ $SYSTEM = "FreeBSD" ] ; then
echo "FreeBSD"
elif [ $SYSTEM = "Solaris" ] ; then
echo "Solaris"
else
echo "What?"
fi
l #關(guān)于while使用的幾個例子
n #無條件循環(huán)
while : ; do
echo "do something forever here"
sleep 5
done
linux常用命令
以下只說明各指令的基本用法, 若需詳細說明, 請用 man 去讀詳細的 manual.
關(guān)于文件/目錄處理的指令:
1. ls
這是最基本的文件指令。 ls 的意義為 "list",也就是將某一個目錄或是某一個文件的內(nèi)容顯示出來。
如果你在下 ls 指令后面沒有跟任何的文件名,它將會顯示出目前目錄中所有文件。
也可以在 ls 后面加上所要察看的目錄名稱或文件的名稱,如:
% ls /home2/X11R5
% ls first
ls 有一些特別的參數(shù),可以給予使用者更多有關(guān)的信息,如下:
-a : 在 UNIX 中若一個目錄或文件名字的第一個字符為 "." , 則使用 ls
將不會顯示出這個文件的名字,我們稱此類文件為隱藏文件。如果我們要察看這類文件,則必須加上參數(shù) -a 。
-l : 這個參數(shù)代表使用 ls 的長( long )格式,可以顯示更多的信息,如文件存取權(quán),文件擁有者( owner ),文件大小,文件最后更新日期,甚而 symbolic link 的文件是 link 到那一個文件等等。例如:
% ls -l
drwx--x--x 2 jjtseng 512 Aug 8 05:08 18
drwx--x--x 2 jjtseng 512 Aug 8 22:00 19
-rw------- 1 jjtseng 566 Aug 8 05:28 makefile
2. cp
cp 這個指令的意義是復制("COPY") , 也就是將一個或多個文件復制成另一個文件或者是將其復制到另一個目錄去。
cp 的用法如下:
cp f1 f2 : 將文件名為 f1 的文件復制一份為文件名為 f2 的文件。
cp f1 f2 f3 ... dir : 將文件 f1 f2 f3 ... 都以相同的文件名復制一份放到目錄 dir 里面。
cp -r dir1 dir2 : 將 dir1 的全部內(nèi)容全部復制到 dir2 里面。
cp 也有一些參數(shù),如下:
-i : 此參數(shù)是當已經(jīng)有文件名為 f2 的文件時,若逕自使用 cp 將會將原來 f2的內(nèi)容覆蓋,因此在要覆蓋之前先詢問使用者。如使用者的回答是y(yes)才執(zhí)行復制的動作。
-r : 此參數(shù)是用來做遞回復制用,可遞歸的將整個目錄都復制到另一個目錄中。
3. mv
mv 的意義為 move , 主要是將一文件改名或移動到另一個目錄。與cp類似,它也有三種格式:
mv f1 f2 : 將文件名為 f1 的文件變更成文件名為 f2 的文件。
mv dir1 dir2 : 將文件名為 dir1 的目錄變更成文件名為 dir2 的目錄。
mv f1 f2 f3 ... dir : 將文件 f1 f2 f3 ... 都移至目錄 dir 里面。
mv 的參數(shù)有兩個,-f 和 -i , 其中 -i 的意義與 cp 中的相同,均是 interactive的意思。而 -f 為強迫( force ) , 就是不管有沒有同名的文件,反正就是要執(zhí)行,所有其他的參數(shù)遇到 -f 均會失效。
4. rm
rm 的意義是 remove ,也就是用來刪除一個文件的指令。需要注意的是,在 UNIX 中一個被刪除的文件除非是系統(tǒng)恰好做了備份,否則是無法像 DOS 里面一樣還能夠恢復的。所以在做 rm 動作的時候使用者應(yīng)該要特別小心。
rm 的格式如下:
rm f1 f2 f3 .....
rm 的參數(shù)比較常用的有幾個: -f , -i , 與 -r
-f : 將會使得系統(tǒng)在刪除時,不提出任何警告訊息。
-i : 在刪除文件之前均會詢問是否真要刪除。
-r : 遞歸的刪除,可用于刪除目錄。
5. mkdir
mkdir 是一個讓使用者建立一個目錄的指令。你可以在一個目錄底下使用 mkdir 建立一個子目錄,使用的方法如下:
mkdir dirname1 [ dirname2 ... ]
8. pwd
pwd 會將當前目錄的路徑( path )顯示出來
9. cat/more/less
以上三個指令均為察看文件內(nèi)容的指令。cat 的意義是concatenate,實際就是把文件的內(nèi)容顯示出來的意思。
cat 有許多奇怪的參數(shù),較常為人所使用的是 -n 參數(shù),也就是把顯示出來的內(nèi)容加上行號。
cat 的用法如下:
cat [-n] :自標準輸入讀取內(nèi)容,可以用 pipe 將別的指令的輸出轉(zhuǎn)向給 cat 。
cat [-n] filename : 將 filename 的內(nèi)容讀進來,顯示在標準輸出上。
問題在於 cat 它是不會停下來的,因此并不好用( 試想如果一個螢?zāi)欢男?,而一個文件四百行,cat 一出來將會噼里啪啦不斷的卷上去,使用者很難據(jù)此得到他們所需的信息。) 所以常常么使用 more 和less來幫助。
more 可以讓文件根據(jù)控制臺的模式一頁頁的顯示出來,再根據(jù)使用者的要求換頁或者換行。如果使用者要在某一個文件中搜尋一個特定的字符串,則按 / 然后跟著打所要搜尋的單字即可進行搜尋。
more 的使用法如下:
more filename
如果你在使用中覺得已經(jīng)看到了所要看的部份,可以按'q'離開 more 的使用。
less 的用法與 more 極類似,原先它就是為了彌補 more 只能往前方卷頁的缺點而設(shè)計。
Less 的用法如下:
less filename
它與 more 不同的是它可以按 y 來往上卷一行,并且可以用"?"來往回搜尋你所要找的字符。
10. chmod
chmod用來改變文件存取模式( change mode ) 。在linux中,一個文件有可讀(r)可寫(w)可執(zhí)行(x)三種模式,分別針對該文件的擁有者( onwer )、同組用戶( group member )(可以 ls -lg來觀看某一文件的所屬的 group ),以及其他人( other )。
一個文件如果改成可執(zhí)行模式則系統(tǒng)就將其視為一個可執(zhí)行文件,而一個目錄的可執(zhí)行模式代表使用者有進入該目錄之權(quán)利。
chmod使用方式如下:
chmod [ -fR ] mode filename ...
其參數(shù)的意義如下:
-f Force. chmod 不會理會失敗的動作。
-R Recurive. 會將目錄下所有子目錄及文件改為你所要改成的模式。
關(guān)于 Process 處理的指令:
1. ps
ps 是用來顯示目前你的 process 或系統(tǒng) processes 的狀況。
其選項說明如下:
-a 列出包括其他 users 的 process 狀況。
-u 顯示 user - oriented 的 process 狀況 。
-x 顯示包括沒有 terminal 控制的 process 狀況 。
-w 使用較寬的顯示模式來顯示 process 狀況 。
我們可以經(jīng)由 ps 取得目前 processes 的狀況,如 pid , running state 等。
2. kill
kill 指令的用途是送一個 signal 給某一個 process 。因為大部份送的都是用來殺掉 process 的 SIGKILL 或 SIGHUP,因此稱為kill 。
kill 的用法為:
kill [ -SIGNAL ] pid ...
kill -l
SIGNAL 為一個 singal 的數(shù)字,從 0 到 31 ,其中 9 是 SIGKILL ,也就是一般用來殺掉一些無法正常 terminate 的信號。
也可以用 kill -l 來察看可代替 signal 號碼的數(shù)字。
關(guān)于字符串處理的指令:
1. echo
echo 是用來顯示一字符串在標準輸出上。echo -n 則表示當顯示完之后不執(zhí)行換行操作。
2. grep
grep 為一過濾器,它可自一個或多個文件中過濾出具有某個字符串的行,或是從標準輸入過濾出具有某個字符串的行。
grep的用法如下:
grep [-nv] match_pattern file1 file2 ....
-n 把所找到的行在行前加上行號列出
-v 把不包含 match_pattern 的行列出
match_pattern 所要搜尋的字符串
-f 以 pattern_file 存放所要搜尋的字符串
聯(lián)機查詢的指令:
1. man
man 是手冊 ( manual ) 的意思。 UNIX 提供線上輔助( on-line help )的功能,man 就是用來讓使用者在使用時查詢指令、系統(tǒng)調(diào)用、標準庫函數(shù)、各種表格等的使用所用的。
man 的用法如下:
man [-M path] [[section] title ] .....
man [-M path] -k keyword ...
-M path man 所需要的 manual database 的路徑。我們也可以用設(shè)定環(huán)境變數(shù) MANPATH 的方式來取代 -M 選項。
title 這是所要查詢的目標。
section 用一個數(shù)字表示 manual 的分類,通常 1 代表可執(zhí)行指令,2 代表系統(tǒng)調(diào)用( system call ) ,3 代表標準庫函數(shù),等等。
man 在 UNIX 上是一項非常重要的指令,我們在這里所描述的用法僅僅只是一個大家比較常用的用法以及簡單的說明,真正詳細的用法與說明還是要通過使用 man 來得到。
2. who
who 指令是用來查詢目前有那些人在線上。
3. info
info的查詢與man類似。
網(wǎng)絡(luò)運用指令:
1. telnet
telnet 是一個提供 user 通過網(wǎng)絡(luò)連到 remote host。
telnet 的 格式如下:
telnet [ hostname | ip-address ] [ port ]
hostname 為一個像 ccsun1 或是 ccsun1.cc.nctu.edu.tw 的 name address,ip-address 則為一個由四個小于 255 的數(shù)字組成的 ip address ,
2. ftp
ftp 的意義是 File Transfer Program ,是一個很常用的在網(wǎng)路文件傳輸?shù)能浖?br />
ftp 的格式如下:
ftp [ hostname | ip-address ]
其中 hostname | ip-address 的意義跟 telnet 中的相同。
在進入 ftp 之后,如果與 remote host 連接上了,它將會詢問你 username 與密碼,如果輸入對了就可以開始進行文件傳輸。
在 ftp 中有許多的命令,這里僅列出較常用的 cd , lcd , mkdir , put , mput , get , mget , binary , ascii , prompt , help 與 quit 的使用方式。
ascii 將傳輸模式設(shè)為 ascii 模式。通常用於傳送文字文件。
binary 將傳輸模式設(shè)為 binary 模式,通常用于傳送執(zhí)行文件,壓縮文件與影像文件等。
cd remote-directory 將 remote host 上的工作目錄改變。
lcd [ directory ] 更改 local host 的工作目錄。
ls [ remote-directory ] [ local-file ] 列出 remote host 上的文件。
get remote-file [ local-file ] 取得登陸機的文件。
mget remote-files 可使用通用字符一次取得多個文件。
put local-file [ remote-file] 將 local host 的文件送到 remote host。
mput local-files 可使用通用字符一次將多個文件放到 remote host 上。
help [ command ] 線上輔助指令。
mkdir directory-name 在 remote host 新建一個目錄。
prompt 更改交談模式,若為 on 則在 mput 與 mget 時每做一個文件傳輸時均會詢問。
Exit/quit離開ftp
3.rlogin命令
rlogin 是“remote login”(遠程登錄)的縮寫。該命令與telnet命令很相似,允許用戶啟動遠程系統(tǒng)上的交互命令會話。
rlogin 的一般格式是:
rlogin [ -8EKLdx ] [ -e char ] [-k realm ] [ - l username ] host
一般最常用的格式是:
rlogin host
該命令中各選項的含義為:
-8 此選項始終允許8位輸入數(shù)據(jù)通道。該選項允許發(fā)送格式化的ANSI字符和其他的特殊代碼。如果不用這個選項,除非遠端的終止和啟動字符不是或,否則就去掉奇偶校驗位。
-E 停止把任何字符當作轉(zhuǎn)義字符。當和-8選項一起使用時,它提供一個完全的透明連接。
-K 關(guān)閉所有的Kerberos確認。只有與使用Kerberos 確認協(xié)議的主機連接時才使用這個選項。
-L 允許rlogin會話在litout模式中運行。要了解更多信息,請查閱tty聯(lián)機幫助。
-d 打開與遠程主機進行通信的TCP sockets的socket調(diào)試。要了解更多信息,請查閱setsockopt的聯(lián)機幫助。
-e 為rlogin會話設(shè)置轉(zhuǎn)義字符,默認的轉(zhuǎn)義字符是“~”,用戶可以指定一個文字字符或一個\nnn形式的八進制數(shù)。
-k 請求rlogin獲得在指定區(qū)域內(nèi)的遠程主機的Kerberos許可,而不是獲得由krb_realmofhost(3)確定的遠程主機區(qū)域內(nèi)的遠程主機的Kerberos 許可。
-x 為所有通過rlogin會話傳送的數(shù)據(jù)打開DES加密。這會影響響應(yīng)時間和CPU利用率,但是可以提高安全性。
4.rsh命令
rsh是“remote shell”(遠程 shell)的縮寫。 該命令在指定的遠程主機上啟動一個shell并執(zhí)行用戶在rsh命令行中指定的命令。如果用戶沒有給出要執(zhí)行的命令,rsh就用rlogin命令使用戶登錄到遠程機上。
rsh命令的一般格式是:
rsh [-Kdnx] [-k realm] [-l username] host [command]
一般常用的格式是:
rsh host [command ]
command可以是從shell提示符下鍵人的任何Linux命令。
rsh命令中各選項的含義如下:
-K 關(guān)閉所有的Kerbero確認。該選項只在與使用Kerbero確認的主機連接時才使用。
-d 打開與遠程主機進行通信的TCP sockets的socket調(diào)試。要了解更多的信息,請查閱setsockopt的聯(lián)機幫助。
-k 請求rsh獲得在指定區(qū)域內(nèi)的遠程主機的Kerberos許可,而不是獲得由krb_relmofhost(3)確定的遠程主機區(qū)域內(nèi)的遠程主機的Kerberos許可。
-l 缺省情況下,遠程用戶名與本地用戶名相同。本選項允許指定遠程用戶名,如果指定了遠程用戶名,則使用Kerberos 確認,與在rlogin命令中一樣。
-n 重定向來自特殊設(shè)備/dev/null的輸入。
-x 為傳送的所有數(shù)據(jù)打開DES加密。這會影響響應(yīng)時間和CPU利用率,但是可以提高安全性。
Linux把標準輸入放入rsh命令中,并把它拷貝到要遠程執(zhí)行的命令的標準輸入中。它把遠程命令的標準輸出拷貝到rsh的標準輸出中。它還把遠程標準錯誤拷貝到本地標準錯誤文件中。任何退出、中止和中斷信號都被送到遠程命令中。當遠程命令終止了,rsh也就終止了。
5.rcp命令
rcp代表“remote file copy”(遠程文件拷貝)。該命令用于在計算機之間拷貝文件。
rcp命令有兩種格式。第一種格式用于文件到文件的拷貝;第二種格式用于把文件或目錄拷貝到另一個目錄中。
rcp命令的一般格式是:
rcp [-px] [-k realm] file1 file2 rcp [-px] [-r] [-k realm] file
directory 每個文件或目錄參數(shù)既可以是遠程文件名也可以是本地文件名。遠程文件名具有如下形式:rname@rhost:path,其中rname是遠程用戶名,rhost是遠程計算機名,path是這個文件的路徑。
rcp命令的各選項含義如下:
-r 遞歸地把源目錄中的所有內(nèi)容拷貝到目的目錄中。要使用這個選項,目的必須是一個目錄。
-p 試圖保留源文件的修改時間和模式,忽略umask。
-k 請求rcp獲得在指定區(qū)域內(nèi)的遠程主機的Kerberos 許可,而不是獲得由krb_relmofhost(3)確定的遠程主機區(qū)域內(nèi)的遠程主機的Kerberos許可。
-x 為傳送的所有數(shù)據(jù)打開DES加密。這會影響響應(yīng)時間和CPU利用率,但是可以提高安全性。 如果在文件名中指定的路徑不是完整的路徑名,那么這個路徑被解釋為相對遠程機上同名用戶的主目錄。如果沒有給出遠程用戶名,就使用當前用戶名。如果遠程機上的路徑包含特殊shell字符,需要用反斜線(\)、雙引號(”)或單引號(’)括起來,使所有的shell元字符都能被遠程地解釋。 需要說明的是,rcp不提示輸入口令,它通過rsh命令來執(zhí)行拷貝。
Vi常用技巧
l 取消命令
在vi中,只要沒有把修改結(jié)果存入磁盤文件中,那么就可以通過“取消”來撤銷最近的操作或?qū)彌_區(qū)的修改。
假設(shè)你無意刪除了一行文本、改變了一些你不應(yīng)該改變的內(nèi)容或增加了一些不正確的文本,可以按<Esc>改變到命令模式中,然后按<u>,則文件內(nèi)容恢復到修改前的樣子。
l 保存到文件名為filename的文件中
發(fā)出寫命令格式: :w filename
l 不使用小鍵盤來定位光標
vi用<h>、<j>、<k>、<l>鍵來定位光標。其中<h>、<l>鍵表示光標的左右移動,<j>、<k>鍵表示光標的上下移動,在某些沒有或不能使用小鍵盤的情況下這四個鍵是很有用的。
下面是其他一些用于移動光標的鍵:
n 按空格鍵或<l>向右移動光標一個位置
n 按<Return>將光標移動到下一行的行首
n 使用<j>鍵將光標移動到下一行的當前位置或行末
n 按<->將光標移動到上一行行首
n 使用<k>鍵將光標移動到上一行的當前位置或行末
n 按<h>將光標向左移動一個字符
n 按<0>(零)將光標移動到一行的行首
n 按<$>將光標移動到一行的行末
l 大范圍移動鍵
可快速定位光標到屏幕的頂部、中部和底部:
n 按<Shift-h>將光標移到屏幕的第一行,有時稱為home位置
n 按<Shift-m>將光標移到現(xiàn)在屏幕顯示的各行的中間一行
n 按<Shift-l>將光標移到屏幕的最后一行
n 按<Ctrl-f>向前移動一屏
n 按<Ctrl-b>向后移動一屏
n 要移動到緩沖區(qū)中指定的行中,在按<Shift-g>前鍵入行號(注意,這里的行號不是當前屏幕中的相對行號,而是絕對行號)
l 刪除文本
n <x>刪除光標處的字符
n <d> <w> 刪除從當前字的光標處到下一個字的開始處之間的內(nèi)容
n <d> <$> 刪除從光標處到行尾之間的內(nèi)容
n <Shift-d> 同<d> <$>,刪除當前行的剩余部分
n <d> <d> 刪除整行,不管光標在該行的位置
n 通過在上述命令之前鍵入一個整數(shù),可將這些命令應(yīng)用到幾個對象中,例如:<4> <x>刪除4個字符;<8> <d> <d> 刪除8行
l 添加文本
n 使用<i>在光標位置前插入文本
n 使用<Shift-i>使你進入輸入模式并且在當前行行首插入文本
n 使用<a>在光標位置后插入文本
n 使用<Shift-a>使你進入輸入模式并且在當前行末尾插入文本
n 使用<o>在當前行的下面打開一行以添加文本
n 使用<Shift-o>在當前行的上面打開一行以添加文本
l 使vi顯示行號
按<Esc>鍵以確保你在命令模式中,然后輸入:se number。要關(guān)閉行號,輸入:se nonumber
l 查找
n /string 在緩沖區(qū)中向前查找字符串string
n ?string 在緩沖區(qū)中向后查找字符串string
n <n> 以當前的方向再次查找
n <Shift-n>以相反的方向再次查找
n 注意,查找的字符串中若含有特殊字符的,要使用來進行轉(zhuǎn)意
l 修改和替換文本
n <r> 替換單個字符
n <Shift-r>替換一個字符序列
n <c> <w>修改當前字,從光標處到這個字的字尾
n <c> <e>修改當前字,從光標處到這個字的字尾(與<c> <w>相同)
n <c> <b>修改當前字,從該字的字頭到光標以前的那些字符
n <c> <$>修改一行,從光標處到該行的行尾
n <Shift-c>修改一行,從光標處到該行的行尾(與<c> <$>相同)
n <c> <c>修改整行
n 注意,這些命令的每個命令都使之進入了輸入模式。除使用<r>來替換單個字符外,必須按<Esc>鍵來完成所作的修改并返回命令模式
n 要修改幾個字,在按<c> <w>之前使用一個整數(shù)
l 拷貝、剪切和粘貼
n <y> <w>拷貝從當前字的光標處到下一個字的開始處之間的內(nèi)容
n <y> <$>拷貝從光標處到行尾之間的內(nèi)容
n <Shift-y>拷貝當前行的剩余部分(與<y> <$>相同)
n <y> <y>拷貝整個當前行
n 通過在這些命令前鍵入整數(shù),所有這些命令都可以用于多個對象。
n 當刪除或剪切或拷貝時,刪除或拷貝的對象被保存在通用緩沖區(qū)中,可以使用<p>或<Shift-p>命令將這個緩沖區(qū)中的內(nèi)容粘貼到光標位置。
n <p>命令將對象粘貼到光標位置右邊或光標位置后面
n <Shift-p>命令將對象粘貼到光標位置左邊或光標位置前面
l 重復命令
可以按< . >來重復改變緩沖區(qū)的最后一個命令。
愛華網(wǎng)


