回憶起一件事情:之前用linux尋找中文輸入法的時(shí)候,在百度輸入了fcitx,然后結(jié)果上邊有個(gè),您要找的是不是: 諷刺騰訊 。本來一直記不住這個(gè)輸入法名字,不過以后哥就記住這個(gè)輸入法的名字是怎么拼了,感謝百度。
第九章awk的驚人表現(xiàn)
awk的調(diào)用可以定義變量、提供程序并且指定輸入文件,語法:
復(fù)制代碼 代碼如下:
awk [ -F fs ] [ -v var=value ... ] 'program' [ -- ] [ var=value ... ] [file(s) ]
awk [ -F fs ] [ -v var=value ... ] -f programfile [ -- ] [ var=value ... ] [ file(s) ]
短程序通常直接在命令行上提供,而比較長的程序則委托-f選項(xiàng)指定,可以重復(fù)使用此選項(xiàng)。如果命令行未指定文件名,則awk會(huì)從標(biāo)準(zhǔn)輸入讀取。 -- 是特殊選項(xiàng),指出awk本身已經(jīng)沒有更進(jìn)一步的命令行選項(xiàng)。任何接下來的選項(xiàng)都可被你的程序使用。
-F選項(xiàng)是用來重新定義默認(rèn)字段分隔字符,且一般慣例將它作為第一個(gè)命令選項(xiàng)。緊接-F選項(xiàng)后的fs參數(shù)是一個(gè)正則表達(dá)式或是被提供作為下一個(gè)參數(shù)。字段分隔字符也可以設(shè)置使用內(nèi)建變量FS所指定。如:
awk -F '\t' '{ ... }' files FS="[\f\v]" files
上邊例子-F選項(xiàng)設(shè)置的值,應(yīng)用到第一個(gè)文件組,而由FS指定的值,則應(yīng)用到第二個(gè)組。初始化的-v選項(xiàng)必須放在命令行上直接給定的任何程序之前,他們會(huì)在程序啟動(dòng)前生效。在一命令行程序之后-v選項(xiàng)會(huì)被解釋為一個(gè)文件名。在命令行上其他地方的初始化會(huì)在處理參數(shù)時(shí)完成,并且會(huì)帶上文件名,如:
awk '{...}' Pass=1 *.tex Pass=2 *.tex
處理文件的列表兩次,第一次Pass設(shè)為1,第二次為2。使用字符串值進(jìn)行初始化無須用引號框起來,除非shell要求這樣的引用以保護(hù)特殊字符或空白。
特殊文件名-(連字符)表示標(biāo)準(zhǔn)輸入。大部分現(xiàn)代的awk實(shí)現(xiàn)(不包括POSIX)都認(rèn)定特殊名稱/dev/stdin為標(biāo)準(zhǔn)輸入,即使主機(jī)操作系統(tǒng)不支持該文件名。同樣:/dev/stderr與/dev/stdout可用于awk程序內(nèi),分別表示標(biāo)準(zhǔn)錯(cuò)誤輸出與標(biāo)準(zhǔn)輸出。
一般awk命令模式或操作可省略一個(gè),如果模式省略,則每條輸入都被操作;如果操作省略,則默認(rèn)操作為輸出匹配模式的記錄。雖然模式多半是數(shù)字或字符串表達(dá)式,不過awk以保留自BEGIN與END提供兩種特殊模式。
與BEGIN關(guān)聯(lián)的操作只會(huì)執(zhí)行一次,在任何命令行文件或一般命令行賦值被處理之前,但是在任何開頭的-v選項(xiàng)指定已完成之后。它大部分是用來處理程序所需要的任何特殊初始化工作。END操作也是只執(zhí)行一次。用于所有輸出數(shù)據(jù)已被處理完之后。BEGIN和END模式可以是任意順序,可以存在awk程序內(nèi)任何位置。當(dāng)指定多個(gè)BEGIN或END模式,則他們將按照在awk程序里的順序執(zhí)行。
awk提供了標(biāo)量與數(shù)組兩種變量以保存數(shù)據(jù)、數(shù)字與字符串表達(dá)式,還提供了一些語句類型以處理數(shù)據(jù):賦值、注釋、條件、函數(shù)、輸入、循環(huán)及輸出。awk表達(dá)式許多功能與c語言相似。awk里注釋是從#開始到行尾??缧姓Z句需要在結(jié)尾處加上反斜杠。
awk里的字符串常數(shù)是以引號定界,字符串可包含任何8bit的字符除了控制字符NUL以外。因?yàn)镹UL在底層實(shí)現(xiàn)語言(C)里,扮演的是一個(gè)字符串中斷字符的角色。awk字符串長度視內(nèi)存而定。反斜杠轉(zhuǎn)義序列允許非打印字符的表示。
awk提供了許多內(nèi)建函數(shù),可以在字符串上執(zhí)行,之后再詳細(xì)說,這會(huì)說兩個(gè)length(string)返回string內(nèi)的字符數(shù)。字符串的比較用的是傳統(tǒng)的關(guān)系運(yùn)算符:==、!=、、=、>、>=。比較不同長度的字符串,且其中一個(gè)字符串為另一個(gè)的初始子字符串時(shí),較短的定義為小于較長的那個(gè)。在shell里字符串連接可以直接進(jìn)行,不需要連接符號。
awk功能強(qiáng)大的地方大多來自于它對正則表達(dá)式的支持。有兩個(gè)運(yùn)算符:~(匹配)與!~(不匹配)讓awk更容易使用正則表達(dá)式:"ABC" ~ "^[A-Z]+$"結(jié)果為真,正則表達(dá)式常量可以用引號或斜杠加以定界:/^[A-Z]+$/。注意如果有字面意義的符號,需要反斜杠來轉(zhuǎn)義。
awk里的數(shù)字,都以雙精度浮點(diǎn)值表示,如1/32 寫成0.03125、3.125e-2等,awk里沒有提供字符串轉(zhuǎn)數(shù)字的函數(shù),不過想做到也很簡單,只要加個(gè)零到字符串里,如:s = "123" , n = 0 + s 。這樣123便賦值給n了。一般"+123ABC"轉(zhuǎn)化為123,而"ABC123"與""都轉(zhuǎn)化為0。即使awk里所有的數(shù)值運(yùn)算都是在浮點(diǎn)算術(shù)內(nèi)完成,整數(shù)值還是可以表示的,只要值不太大,這個(gè)值限定在53位,即2^53即9千萬億的樣子。awk的數(shù)值運(yùn)算符沒有位運(yùn)算符,多一個(gè)指數(shù)運(yùn)算符(^ 或 ** 或 **=,但是避免使用**和*=,它不是POSIX awk的一部分)它是右結(jié)合性的,且與賦值運(yùn)算符是僅有的右結(jié)合性運(yùn)算符。比如a^b^c^d運(yùn)算順序是a^(b^(c^d))。awk里的取余運(yùn)算測試了 5 % 3 是2 ; 5 % -3 是2; -5 % 3 是-2; -5 % -3是-2;發(fā)現(xiàn)取余的結(jié)果取決于被取余的數(shù)的正負(fù)。還有一個(gè)內(nèi)建函數(shù):
int(x) 對x取整
rand 取 0到1之間的隨機(jī)數(shù)
srand(x) 設(shè)置x為rand的新輸入值
cos(x) 給出x的余弦值
sin(x) 給出x的正弦值
atan2(x,y) 給出y/x的正切值
exp(x) 給出e的x次冪
log(x) 給出x的常用對數(shù)值(基為e)
sqrt(x) 給出x的正平方根值
exit(x) 結(jié)束awk程序,若有x值,則返回x,否則返回0.
index(s,t) 返回t在s中的第一個(gè)開始位置,如t不是s的子串,則返回0]
length(x) 求x的長度(字符個(gè)數(shù))
substr(s,x,y) 在字符串s中取得從x個(gè)字符開始的長度為y的子字符串.
awk內(nèi)置字符串函數(shù)
gsub(r,s) 在整個(gè)$0中用s替代r
gsub(r,s,t) 在整個(gè)t中用s替代r
index(s,t) 返回s中字符串t的第一位置
length(s) 返回s長度
match(s,r) 測試s是否包含匹配r的字符串
split(s,a,fs) 在fs上將s分成序列a
sprint(fmt,exp) 返回經(jīng)fmt格式化后的exp
sub(r,s) 用$0中最左邊最長的子串代替s
substr(s,p) 返回字符串s中從p開始的后綴部分
substr(s,p,n) 返回字符串s中從p開始長度為n的后綴部分
awk提供許多內(nèi)建變量,都是大寫名稱,時(shí)常用到的幾個(gè)有:
FILENAME 當(dāng)前輸入文件的名稱
FNR 當(dāng)前輸入文件的記錄數(shù)
FS 字段分隔字符(正則表達(dá)式)(默認(rèn)為:" ")
NF 當(dāng)前記錄的字段數(shù)
NR 在工作中的記錄數(shù)
OFS 輸出字段分隔字符(默認(rèn)為:" ")
ORS 輸出記錄分隔字符(默認(rèn)為:"\n")
RS 輸入記錄分隔字符(僅用于gawk與mawk里的正則表達(dá)式)(默認(rèn)為:"\n")
awk允許的測試:
x==y x等于y?
x!=y x不等于y?
x>y x大于y?
x>=y x大于或等于y?
x x=y x小于或等于y?
x~re x匹配正則表達(dá)式re?
x!~re x不匹配正則表達(dá)式re?
awk的操作符
= 、+=、 -=、 *= 、/= 、 %=
|| > >= = == != ~ !~
xy (字符串連結(jié),'x''y'變成"xy")
+ - * / % ++ --
awk沒有提供位操作符,但是提供了相關(guān)的函數(shù):
and(v1, v2) Return the bitwise AND of the values provided by v1 and v2.
compl(val) Return the bitwise complement of val.
lshift(val, count) Return the value of val, shifted left by count bits.
or(v1, v2) Return the bitwise OR of the values provided by v1 and v2.
rshift(val, count) Return the value of val, shifted right by count bits.
xor(v1, v2) Return the bitwise XOR of the values provided by v1 and v2.
awk的數(shù)組變量允許數(shù)組名稱之后,以方括號將任意數(shù)字或字符串表達(dá)式括起來作為索引。以任意值為索引的數(shù)組稱之為關(guān)聯(lián)數(shù)組。awk將應(yīng)用于數(shù)組中,允許查找插入和刪除等操作,在一定時(shí)間內(nèi)完成,與存儲(chǔ)多少項(xiàng)目無關(guān)。(說了這么多其實(shí)就是hash數(shù)組)。delete array[index]會(huì)從數(shù)組中刪除元素。delete array刪除整個(gè)數(shù)組。awk數(shù)組還可以這么用:
print maildrop[53, "Oak Lane", "T4Q 7XV"]
print maildrop["53" SUBSEP "Oak Lane" SUBSEP "T4Q 7XV"]
print maildrop["53\034Oak Lane", "T4Q 7XV"]
print maildrop["53\034Oak Lane\034T4Q 7XV"]
以上輸出結(jié)果都是一樣的。內(nèi)建變量SUBSEP默認(rèn)值是\034,可以更改它。如果稍后更改了SUBSEP的值,將會(huì)使已經(jīng)存儲(chǔ)數(shù)據(jù)的索引失效,所以SUBSEP其實(shí)應(yīng)該在每個(gè)程序只設(shè)置一次,在BEGIN操作里。
awk對于命令行的自動(dòng)化處理,意味著awk程序幾乎不需要關(guān)心他們自己。awk通過內(nèi)建變量ARGC(參數(shù)計(jì)數(shù))與ARGV(參數(shù)向量,或參數(shù)值),讓命令行參數(shù)可用。給出例子說明其用法:
復(fù)制代碼 代碼如下:
$ cat >showargs.awk
BEGIN{
print "ARGC = ",ARGC
for ( k = 0 ; k ARGC ; k++)
print "ARGV[" k "] = [" ARGV[k] "]"
}
$ awk -v One=1 -v Two=2 -f showargs.awk Three=3 file1 Four=4 file2 file3
ARGC = 6
ARGV[0] = [awk]
ARGV[1] = [Three=3]
ARGV[2] = [file1]
ARGV[3] = [Four=4]
ARGV[4] = [file2]
ARGV[5] = [file3]
正如C/C++中,參數(shù)存儲(chǔ)在數(shù)組項(xiàng)目0、1....、ARGC-1中,第0個(gè)項(xiàng)目是awk程序本身的名稱。不過與-f 和 -v選項(xiàng)結(jié)合性的參數(shù)是不可使用的。同樣的,任何命令行程序也不可使用:
復(fù)制代碼 代碼如下:
$ awk 'BEGIN{for(k=0;kARGC;k++)
print "ARGV["k"] = ["ARGV[k]"]"}' a b c
ARGV[0] = [awk]
ARGV[1] = [a]
ARGV[2] = [b]
ARGV[3] = [c][/c][/c]
是否需要顯示在程序名稱里的目錄路徑,則看實(shí)際情況而定。awk程序可修改ARGC和ARGV,注意保持倆個(gè)的一致性。
awk一見到參數(shù)含有程序內(nèi)容或是特殊--選項(xiàng)時(shí),它會(huì)立即停止將參數(shù)解釋為選項(xiàng)。任何接下來的看起來像是選項(xiàng)的參數(shù),都必須由你的程序處理,并接著從ARGV中被刪除或設(shè)置為空字符串。
awk提供訪問內(nèi)建數(shù)組ENVIRON中所有的環(huán)境變量:
復(fù)制代碼 代碼如下:
$ awk 'BEGIN{ print ENVIRON["HOME"]; print ENVIRON["USER"]}'
/home/administrator
administrator
ENVIRON數(shù)組并無特別之處,可以隨意修改刪除。然而,POSIX要求子進(jìn)程繼承awk啟動(dòng)時(shí)生效的環(huán)境,而我們也發(fā)現(xiàn),在現(xiàn)行實(shí)現(xiàn)下,并無法將對于ENVIRON數(shù)組的變更傳遞給子進(jìn)程或者內(nèi)建函數(shù)。特別地,這是指你無法通過對EVNIRON["LC_ALL"]的更改控制字符串函數(shù),例如tolower(),在特定locale下的行為模式。因此你應(yīng)將ENVIRON看成一個(gè)只讀數(shù)組。如果要控制子進(jìn)程的locale,則可通過在命令行字符串里設(shè)置適合的環(huán)境變量達(dá)成。如:
system("env LC_ALL=es_Es sort infile > outfile")#以Spanish的locale排序文件。
system()函數(shù)稍后說明。
模式與操作構(gòu)成awk程序的核心。模式為真則進(jìn)行操作。一般模式是正則表達(dá)式,就會(huì)被拿來與整個(gè)輸入記錄進(jìn)行匹配,比如:
NF == 0 #選定空記錄
NF > 3 #選定擁有三個(gè)字段以上的記錄
NR 5 #選定第一到第四條記錄
$1 ~ /jones/ #選定字段1中有jones的記錄
/[xX][mM][lL]/ #忽略大小寫選定含xml的記錄
awk在匹配功能上,還可以使用范圍表達(dá)式,以逗點(diǎn)隔開的兩個(gè)表達(dá)式。比如:
(FNR == 3) , (FNR == 10) #選定每個(gè)輸入文件按里記錄3到10
/[Hh][Tt][Mm][Ll]>/ , /\/[Hh][Tt][Mm][Ll]>/ #選定html文件里的主體
在BEGIN操作里,F(xiàn)ILENAME、FNR、NF與NR初始都未定義;引用到他們時(shí),會(huì)返回null。
通過模式的匹配,就要把為真記錄的傳給操作。給出一些實(shí)例:
#unix單詞計(jì)數(shù)程序wc:
awk '{ C += length($0) + 1 ; W += NF } END { print NR, W, C}'
注意:模式/操作組并不需要以換行字符分隔,一般換行是為了閱讀方便。我們也可以使用BEGIN{ C = W =0} 來初始化,但是awk具有默認(rèn)的初始化保證。
#將原始數(shù)據(jù)值及他們的對數(shù)打印為單欄數(shù)據(jù)文件:
awk ' { print $1 , log($1) }' file(s)
#要從文本文件里隨機(jī)打印5%行左右的樣本:
awk 'rand() 0.05 ' file(s)
#以空白分隔字段的表格中,報(bào)告第n欄的和:
awk -v COLUMN=n '{ sum += $COLUMN } END { print sum } ' file(s)
#產(chǎn)生字段n欄的平均值
awk -v COLUMN=n '{ sum += $COLUMN } END { print sum / NR } ' file(s)
#統(tǒng)計(jì)文件最后一個(gè)字段的總數(shù)
awk '{ sum += $NF; print $0 , sum }' file(s)
#三種查找文件內(nèi)文本的方式:
egrep 'pattern|pattern' file(s)
awk '/pattern|pattern/' file(s)
awk '/pattern|pattern/ { print FILENAME ":" FNR ":" $0 }' file(s)
#僅查找100-150行 的匹配信息
sed -n -e 100,150p -s file(s) | egrep 'pattern'
awk '(100=FNR)(FNR=150) /pattern/ { print FILENAME":"FNR":"$0}' file(s)
#要在四欄表格里調(diào)換二三欄,假設(shè)制表符分隔:
awk -F'\t' -v OFS='\t' '{ print $1,$3,$2,$4}' old > new
awk 'BEGIN {FS=OFS='\t' } {print $1,$3,$2,$4 }' old > new
awk -F'\t' '{ print $1 "\t"$3"\t"$2"\t"$4} ' old > new
#將格欄分隔符由制表符替換成:
sed -e 's/\t/\/g' file(s)
awk 'BEGIN { FS="\t"; OFS="" } {$1 = $1; print }' file(s)
#刪除排序后的重復(fù)行:
sort file(s) | uniq
sort file(s) | awk 'Last != $0 { print } {Last = $0} '
#將回車字符/換行符的行終結(jié),一致轉(zhuǎn)換為以換行字符為行終結(jié):
sed -e 's/\r$//' file(s)
sed -e 's/^M$//' file(s)
mawk 'BEGIN { RS="\r\n" } { print } ' file(s)
#找出長度超過72個(gè)字符的行:
egrep -n '^.{73,}' file(s)
awk 'length($0) > 72 { print FILENAME":"FNR":"$0}' file(s)
awk支持語句的連續(xù)執(zhí)行。支持條件語句,if else 類似C語言,支持循環(huán) while(){} 或do{} while()或for( ; ; ){] 類似c語言。還有一個(gè)for(key in array) { } 。
如 awk 'BEGIN { for( x=0; x=1;x+=0.05) print x}' 。雖然很多類似C,但是注意awk中是缺乏逗點(diǎn)運(yùn)算符的。循環(huán)同樣可以使用break和continue 。
awk直接處理命令行上標(biāo)明的輸入文件,一般不用用戶自己打開與處理文件,但是也可以通過awk的getline語句做這些事情。用法:
getline 從當(dāng)前輸入文件讀取下一條記錄存入$0,并更新NF、NR、FNR
getline var 從當(dāng)前輸入文件中,讀取下一條記錄存入var并更新NR、FNR
getline file 從fle中讀取下一條記錄,存入$0,并更新NF
getline var file 從file讀取下條記錄存入var
cmd | getline 從外部命令cmd讀取下條記錄存入$0,并更新NF
cmd | getline var 從外部命令讀取下條記錄,存入var
如果像確保來自控制終端的輸入則:getline var "/dev/tty"
在awk里可以通過管道與外部的shell命令混寫:
復(fù)制代碼 代碼如下:
tmpfile = "/tmp/telephone.tmp"
comman = "sort > " tmpfile
for ( name in telephone)
print name "\t" telephone[name] | command
close (command)
while((getline tmpfile) > 0)
print
close(tmpfile)
close可以關(guān)閉打開的文件以解約可用資源。awk里也沒有排序函數(shù),以為它只需要復(fù)制功能強(qiáng)大的sort命令即可。
getline語句以及awk管道里的輸出重定向都可與外部程序通信,system(command)函數(shù)提供的是第三種方式:其返回值是命令的退出碼。所以上邊的例子可以寫成:
復(fù)制代碼 代碼如下:
tmpfile = "/tmp/telephone.tmp"
for ( name in telephone)
print name "\t" telephone[name] | > tmpfile
close (tmpfile)
system("sort " tmpfile)
while((getline tmpfile) > 0)
print
close(tmpfile)
對于被system()執(zhí)行的命令并不需要調(diào)用close(),因?yàn)閏lose()僅針對以I/O重定向運(yùn)算符所打開的文件或管道,還有g(shù)etline、print、printf。其他幾個(gè)例子:
system("rm -f " tmpfile)
system("cat 由于每次調(diào)用system()都會(huì)起始一個(gè)全新的shell,因此沒有簡單方式可以在分開的system()調(diào)用內(nèi)的命令之間傳遞數(shù)據(jù),除非通過中間文件。
就到目前這里,awk足夠編寫任何數(shù)據(jù)處理程序了。對于大型程序,不利于維護(hù)和查看,所以awk提供函數(shù),就像c一樣,awk也可選擇性的返回標(biāo)量值。函數(shù)可以定義在程序頂層的任何位置:成對的模式/操作組之前、之間、之后。在單一文件的程序里,慣例是將所有函數(shù)放在成對的模式/操作碼之后,且讓他們依字母順序排列,這樣會(huì)讀起來方便。定義如下:
function name(arg1,arg2....){ statement(s) ; return expression ;}
局部的變量會(huì)覆蓋全局的同名變量。
awk里其他的內(nèi)建函數(shù):
子字符串提取substr(string,start,len),下標(biāo)從1開始。
字母大小寫轉(zhuǎn)換tolower(string),toupper(string)。無法處理罕見字母和重音字母。
字符查找index(string,find),返回起始位置,找不到給0.
字符串匹配match(string,regexp),匹配則返回string的索引,并且會(huì)更新全局變量RSTART和RLENGTH,獲取匹配方法:substr(string, RSTART,RLENGTH)。
字符串替換sub(regexp,replacement,target)和gsub(regexp,replacement,target)。前者將target與正則表達(dá)式進(jìn)行匹配,將最左邊最長的匹配部分替換為字符串。
gsub()的運(yùn)行類似,不過它會(huì)替換所有匹配的字符串。兩種函數(shù)都返回替換的數(shù)目。如果省略第三個(gè)參數(shù),則默認(rèn)值為當(dāng)前的記錄$0。兩個(gè)函數(shù)里replacement里的字符都會(huì)被替換為target中與regexp匹配的文本。使用\可關(guān)閉這一功能,而且請記得如果你要在引號字符串里使用它時(shí),以雙斜杠轉(zhuǎn)義它。如gsub(/[aeiouyAEIOUY]/,"")令所有當(dāng)前$0里的元音字母乘以兩倍,而gsub(/[aeiouyAEIOUY]/,"\\\\")則是將所有元音字母替換為一對符號。
字符串分割:awk針對$0自動(dòng)提供了方便的分割為$1 $2 .... $NF,也可以函數(shù)來做:split(string,array,regexp)將string切割為片段,并存儲(chǔ)到array里。如果regexp省略,則默認(rèn)內(nèi)建字段分隔符為FS。函數(shù)返回array里的元素?cái)?shù)量。填寫分割符的時(shí)候留意默認(rèn)字段分隔符" "與"[ ]"的差異:前者會(huì)忽略前置與結(jié)尾的空白,并于運(yùn)行時(shí)將空白視為一個(gè)單獨(dú)空格,后者則正好匹配一個(gè)空格,對絕大多數(shù)文本處理而言,第一種模式已經(jīng)滿足功能上的需求了。
字符串格式化sprintf(format,expression1,expression2,...) ,它會(huì)返回已格式化的字符串作為其函數(shù)值。printf()的運(yùn)行方式也是這樣,只不過它會(huì)在標(biāo)準(zhǔn)輸出或重定向的文件上顯示格式化后的字符串,而不是返回其函數(shù)值。這倆函數(shù)類似shell里的printf,但是還有些許差異,使用的時(shí)候注意一下。
數(shù)值函數(shù):
atan2(y,x) 返回y/x的反正切
exp(x) 返回x的指數(shù),ex
int(x),log(x),cos(x),sin(x),sqrt(x),
rand() 返回0=r1
srand(x) 設(shè)置虛擬隨機(jī)產(chǎn)生器的種子為x,并返回正確的種子。如果省略x,則使用當(dāng)前時(shí)間(以秒計(jì))。如果srand()未被調(diào)用,則awk每次執(zhí)行都會(huì)從默認(rèn)種子開始。
awk內(nèi)置變量(預(yù)定義變量)
說明:表中v項(xiàng)表示第一個(gè)支持變量的工具(下同):A=awk,N=nawk,P=POSIX awk,G=gawk
V 變量 含義 缺省值
--------------------------------------------------------
N ARGC 命令行參數(shù)個(gè)數(shù)
G ARGIND 當(dāng)前被處理文件的ARGV標(biāo)志符
N ARGV 命令行參數(shù)數(shù)組
G CONVFMT 數(shù)字轉(zhuǎn)換格式 %.6g
P ENVIRON UNIX環(huán)境變量
N ERRNO UNIX系統(tǒng)錯(cuò)誤消息
G FIELDWIDTHS 輸入字段寬度的空白分隔字符串
A FILENAME 當(dāng)前輸入文件的名字
P FNR 當(dāng)前記錄數(shù)
A FS 輸入字段分隔符 空格
G IGNORECASE 控制大小寫敏感0(大小寫敏感)
A NF 當(dāng)前記錄中的字段個(gè)數(shù)
A NR 已經(jīng)讀出的記錄數(shù)
A OFMT 數(shù)字的輸出格式 %.6g
A OFS 輸出字段分隔符 空格
A ORS 輸出的記錄分隔符 新行
A RS 輸入的記錄他隔符 新行
N RSTART 被匹配函數(shù)匹配的字符串首
N RLENGTH 被匹配函數(shù)匹配的字符串長度
N SUBSEP 下標(biāo)分隔符 "34"
以上基本上把所有awk的內(nèi)容詳細(xì)講完了,十分的強(qiáng)大,網(wǎng)上搜了些別的關(guān)于awk的講解,沒發(fā)現(xiàn)有哪篇講解像這本書里這么全的。
上邊例子給出的比較少,這里有很多例子可供參考。
第十章文件處理
先講了ls命令,應(yīng)該很熟了,再羅列一下主要選項(xiàng)吧:
-1 數(shù)字1,強(qiáng)制單欄輸出,默認(rèn)的以適合窗口寬度輸出
-a 顯示所有文件
-d 顯示與目錄相關(guān)信息,而非他們包含的文件的信息
-F 使用特殊結(jié)尾字符,標(biāo)記特定的文件類型。試了一下路徑加了斜杠,可執(zhí)行文件加了*號。別的沒怎么試。
-g 僅適用于組:省略所有者名稱
-i 列出inode編號
-L 緊連著符號性連接,列出他們指向的文件。
-l 小寫L,顯示詳細(xì)信息。
-r 倒置默認(rèn)排序
-R 遞歸列出下沿進(jìn)入每個(gè)目錄
-S 按照由大到小的文件大小計(jì)數(shù)排序,僅GNU版本支持。
-s 以塊(與系統(tǒng)有關(guān))為單位,列出文件的大小。
-t 按照最后修改時(shí)間排序
--full-time 顯示完整的時(shí)間戳
說明一下長信息顯示的時(shí)候的內(nèi)容:
drwxrwxr-x 2 administrator administrator 1024 1月 5 10:43 bin
第一個(gè)字母 - 表示一般文件 d表示目錄 l表示符號連接
接下來的9個(gè)字符,每三個(gè)是一組,報(bào)告所有組的權(quán)限,r表示可讀,w表示可寫,x表示可執(zhí)行。前三個(gè)是擁有者選前,中間三個(gè)是用戶所在組的權(quán)限,最后三個(gè)是其他人的權(quán)限。
第二欄包含連接計(jì)數(shù)。第三四欄表示所有者和所屬組。第五欄是字節(jié)單位大小。最后是時(shí)間和文件(夾)名。
書中給了一個(gè)命令od 說顯示真是的文件名,ls | od -a -b ,嘗試了一下,完全看不懂輸出內(nèi)容。貌似是以nl(八進(jìn)制012)做分隔符,然后羅列處來文件名的樣子。如果文件名有漢字,顯示會(huì)是一些符號。各種不懂。
書中用一節(jié)說使用touch更新修改時(shí)間,并說有時(shí)時(shí)間戳是有意義的,但內(nèi)容則否。常見例子是用于鎖定文件,以指出程序已在執(zhí)行中,不應(yīng)該啟動(dòng)第二個(gè)實(shí)例。另一用途則為記錄文件的時(shí)間戳,供日后與其他文件對照用。touch默認(rèn)(-m) 操作會(huì)改變文件的最后修改時(shí)間,也可以使用-a選項(xiàng)改變文件的最后訪問時(shí)間。也可以搭配-t選項(xiàng)修改時(shí)間,方式是加上[[CC]YY]MMDDHHMM[.SS]形式的參數(shù),世紀(jì)、公元年和秒數(shù)是可選項(xiàng),例如:
$ touch -t 201201010000.00 date #建立一個(gè)文件設(shè)定時(shí)間戳
touch還提供-r選項(xiàng),復(fù)制參照文件的時(shí)間戳。
以日期來看,unix時(shí)間戳是從零開始,由1970/1/1/ 00:00:00 UTC算起。
然后又用一節(jié)介紹了一下臨時(shí)文件/tmp 。一般要解決自己程序生成的臨時(shí)文件,共享的目錄或同一程序的多個(gè)執(zhí)行實(shí)例可能造成臨時(shí)文件命名沖突,一般使用的都是進(jìn)程ID,可以在shell變量
復(fù)制代碼 代碼如下:
umask 077 #???????????????
TMPFILE=${TMPDIR-/tmp}/myprog." />You can't use 'macro parameter character #' in math mode #產(chǎn)生臨時(shí)性文件名
trap 'rm -f $TMPFILE' EXIT #完成時(shí)刪除臨時(shí)文件
但是像/tmp/myprog.
復(fù)制代碼 代碼如下:
$ cat $HOME/html2xhtml.sed
s/H1>/h1>/g
...
s:H2>:h2>:g
...
cd top level web site directory
find . -name '*.html' -o -name '*.htm' -type f |
while read file
do
echo $file
mv $file $file.save
sed -f $HOME/html2xhtml.sed $file.save > $file
done
書中說了一小節(jié)尋找問題文件,意思是文件名里有特殊字符,可以實(shí)用find -print0 來解析,但是沒搞明白說這些是干嘛用的。
然后介紹了一個(gè)執(zhí)行命令xargs,是為了處理給腳本傳參過長的問題,不如有時(shí)候我們會(huì)寫尋找字符串的命令如下:
$ grep POSIX_OPEN_MAX /dev/null $(find /usr/include -type f | sort )
我們在后邊一堆文件中尋找 POSIX_OPEN_MAX這樣的一個(gè)字符串。如果后邊f(xié)ind出來的文件很少,那很好,這條命令就會(huì)順利執(zhí)行,但是如果過長會(huì)給出提示:****:Argument list too long. 這樣子。我們可以通過getconf ARG_MAX來查看你的系統(tǒng)允許的最大值是多少。上邊這條命令有一個(gè)文件是空文件/dev/null,這是為了防止find沒找到任何文件使grep進(jìn)入從標(biāo)準(zhǔn)輸入獲取信息的空等狀態(tài),也為了使grep命令有多個(gè)文件參數(shù)而使結(jié)果可以顯示文件名和出現(xiàn)的行數(shù)。
我們可以解決這樣的一個(gè)參數(shù)過長的問題通過開始提到的xargs命令,如:
$ find /usr/include -type f | xargs grep POSIX_OPEN_MAX /dev/null
這里xargs如果未取得輸入文件名,則會(huì)默認(rèn)終止。GNU的xargs支持--null選項(xiàng):可處理GNU find的-print0選項(xiàng)所產(chǎn)生的NUL結(jié)尾的文件名列表。xargs將每個(gè)這樣的文件名作為一個(gè)完整參數(shù),傳遞給它執(zhí)行的命令,而沒有shell(錯(cuò)誤)解釋問題或換行符號混淆的危險(xiǎn),然后是交給其后的命令處理它的參數(shù)。另外xargs的選項(xiàng)可以控制哪些參數(shù)需要被替換,還可以限制傳遞的參數(shù)個(gè)數(shù)等。
如果了解文件系統(tǒng)的空間信息,我們可以通過find和ls命令配合awk程序協(xié)助就可辦到,比如:
$ find -ls | awk '{sum +=$7} END {printf("Total: %.0f bytes\n",sum)}'
但并不好用,編碼長不說還不知道可用空間。有兩個(gè)好用的命令來解決這一需求:df和du。
df(disk free)提供單行摘要,一行顯示一個(gè)加載的文件系統(tǒng)的已使用和可實(shí)用空間。顯示單位具體看相應(yīng)版本??梢詫?shí)用-k強(qiáng)制實(shí)用kilobytes單位。還有一個(gè)選項(xiàng)-l 僅顯示本地文件系統(tǒng),排除網(wǎng)絡(luò)加載的文件系統(tǒng)。還有-i選項(xiàng),提供訪問inode使用量。GNU的df還提供-h(human-readable)選項(xiàng),方便閱讀。還可以提供一個(gè)或多個(gè)文件系統(tǒng)名稱或加載點(diǎn)來限制輸出項(xiàng)目:$ df -lk /dev/sda6 /var 。
du會(huì)摘要文件系統(tǒng)的可用空間,但是不會(huì)告訴你某個(gè)特定的目錄樹需要多少空間,這是du(disk usage)的工作。不同系統(tǒng)可能有所不同,-k控制單位,-s顯示摘要。
GNU版本提供-h,同df。du可以解決的一個(gè)常見問題是:找出哪個(gè)用戶用掉最多的系統(tǒng)空間:$ du -s -k /home/users/* | sort -k1nr | less
假設(shè)用戶目錄全部放在/home/users下。
關(guān)于比較文件好用的兩個(gè)命令cmp和diff。cmp直接后邊跟兩個(gè)文件參數(shù)即可,如果不同輸出結(jié)果會(huì)指出第一個(gè)不同處的位置,相同沒有任何輸出。-s可以抑制輸出,可以通過$?來查看離開狀態(tài)碼,非零表示不同。diff慣例是將舊文件作為第一個(gè)參數(shù),不同的行會(huì)以前置左尖括號的方式,對應(yīng)到左邊文件,而前置右尖括號則指的是右邊的文件。還有一個(gè)擴(kuò)展是diff3,比較3個(gè)文件。
有時(shí)候需要修復(fù)不同的地方,patch命令提供了十分方便的做法:
復(fù)制代碼 代碼如下:
$ echo test 1 > test.1
$ echo test 2 > test.2
$ diff -c test.[12] > test.dif
$ patch test.dif
此時(shí)你查看test.1會(huì)發(fā)現(xiàn)里邊的內(nèi)容已經(jīng)變?yōu)閠est 2了。patch會(huì)盡可能套用不同之處,然后報(bào)告失敗的部分,由用戶自行處理。雖然patch也可以處理一般的diff輸出,但是常規(guī)都是處理diff -c選項(xiàng)的信息。
如果有你懷疑有很多文件有相同的內(nèi)容,實(shí)用cmp或diff就十分麻煩。這時(shí)可以實(shí)用file checksum(文件校驗(yàn)和),取得近似線性的性能完成這一繁瑣的工作。有很多工具可以提供,如:sum、cksum、checksum,消息摘要工具md5與md5sum,安全性三列(secure-hash)算法工具sha、sha1sum、sha256以及sha384??上У氖牵簊um的實(shí)例在各個(gè)平臺(tái)都不想同,使得他們的輸出無法跨越不同unix版本進(jìn)行文件校驗(yàn)和的比較。一般的會(huì)這樣:
$ md5sum /bin/l?
57e35260b8e017f4696b8559ed4f32f2 /bin/ln
0f68a291df1a708e130e0f407a67e9ca /bin/ls
輸出結(jié)果有32個(gè)十六進(jìn)制數(shù),等同128位,因此兩個(gè)不同文件最后散列出相同簽名的可能性非常低。了解這個(gè)后可以寫一個(gè)簡單腳本來實(shí)現(xiàn)我們之前的目標(biāo)了。
復(fù)制代碼 代碼如下:
#! /bin/sh -
# 根據(jù)他們的MD5校驗(yàn)和,顯示某種程度上內(nèi)容機(jī)會(huì)一直的文件名
#
# 語法:
# show-indentical-files files
IFS='
'
PATH=/usr/local/bin:/usr/bin:/bin
export PATH
md5sum "$@" /dev/null 2> /dev/null |
awk '{
count[$1]++
if( count[$1] ==1 ) first[$1]=$0
if( count[$1] ==2 ) print first[$1]
if( count[$1] >1 ) print $0
}' |
sort | awk '{
if ( last != $1 ) print ""
last = $1
print
}'
程序很簡單,就不弄注釋了吧??梢詼y試一下:
$ show-indentical-files /bin/*
發(fā)現(xiàn)好多命令都很能裝啊,其實(shí)內(nèi)容都一樣的 - -!。
這里說一下數(shù)字簽名驗(yàn)證,很有用。
軟件發(fā)布的時(shí)候,一般會(huì)包含分發(fā)文件的校驗(yàn)和,這可以讓你方便得知所下載的文件是否與原始文件匹配。不過單獨(dú)的校驗(yàn)和不能提供驗(yàn)證(verification)工作:如果校驗(yàn)和被記錄在你下載軟件里的另一個(gè)文件中,則攻擊者可以惡意的修改軟件,然后只需要相應(yīng)的修改校驗(yàn)和即可。
這個(gè)問題的解決方案是公鑰加密(public-key cryptography)。在這種機(jī)制下,數(shù)據(jù)的安全保障來自兩個(gè)相關(guān)密鑰的存在:一個(gè)私密密鑰,只有所有者知悉,以及一個(gè)公開密鑰,任何人都可得知。兩個(gè)密鑰的其中一個(gè)用以加密,另一個(gè)則用于解密。公開密鑰加密的安全性,依賴已知的公開密鑰及可被該密鑰解密的文本,以提供一條沒有實(shí)際用途的信息但可被用來回復(fù)私密密鑰。這一發(fā)明最大的突破是解決了一直以來密碼學(xué)上極為嚴(yán)重的問題:在需要彼此溝通的對象之間,如何安全的交換加密密鑰。
私密密鑰與公開密鑰是如何使用和運(yùn)作的呢?假設(shè)Alice想對一個(gè)公開文件簽名,她可以使用她的私密密鑰(private key)為文件加密。之后Bob再使用Alice的公開密鑰(public key)將簽名后的文件解密,這么一來即可確信該文件為Alice所簽名,而Alice無須泄漏其私密密鑰,就能讓文件得到信任。
如果Alice想傳送一份只有Bob能讀的信給他,她應(yīng)以Bob的公開密鑰為信件加密,之后Bob再使用它的私密密鑰將信件解密。只要Bob妥善保管其私密密鑰,Alice便可確信只有Bob能讀取她的信件。
對整個(gè)信息加密其實(shí)是沒有必要的:相對的,如果只有文件的校驗(yàn)和加密,它就等于有數(shù)字簽名(digital signature)了。如果信息本身是公開的,這種方法便相當(dāng)有用,不過還需要有方法驗(yàn)證它的真實(shí)性。要完整說明公開密鑰加密機(jī)制,需要整本書才行,可參考《安全性與密碼學(xué)》。
計(jì)算機(jī)越來越容易受到攻擊,下載文件或軟件要很注意安全。一般軟件存檔文件都并入了文件校驗(yàn)和信息的數(shù)字簽名,如果不確定下載的東西是否安全,可以驗(yàn)證它。舉例:
$ ls -l coreutils-5.0.tar*
-rw-rw-r-- 1 jones devel 6020616 Apr 2 2003 coreutils-5.0.tar.gz
-rw-rw-r-- 1 jones devel 65 Apr 2 2003 coreutils-5.0.tar.gz.sig
$gpg coreutils-5.0.tar.gz.sig #嘗試驗(yàn)證此簽名
gpg: Signature made Wed Apr 2 14:26:58 2003 MST using DSA key ID D333CBA1
gpg: Can't check signature: public key not found
驗(yàn)證失敗,是因?yàn)槲覀冞€未將簽名者的公開密鑰加入gpg密鑰環(huán)。我們可以在簽名作者的個(gè)人網(wǎng)站找到公開密鑰或者通過email詢問。然而幸好使用數(shù)字簽名的人多半會(huì)將他們的公開密鑰注冊到第三方(thrid-party)的公開密鑰服務(wù)器,且該注冊會(huì)自動(dòng)地提供給其他的密鑰服務(wù)器共享。
將密鑰內(nèi)容存儲(chǔ)到臨時(shí)文件如”temp.key",加到密鑰環(huán)中:
$ gpg --import temp.key
然后就可以成功驗(yàn)證簽名了。
您可能感興趣的文章:- shell腳本學(xué)習(xí)指南[二](Arnold Robbins & Nelson H.F. Beebe著)
- shell腳本學(xué)習(xí)指南[一](Arnold Robbins & Nelson H.F. Beebe著)
- shell腳本學(xué)習(xí)指南[六](Arnold Robbins & Nelson H.F. Beebe著)
- shell腳本學(xué)習(xí)指南[五](Arnold Robbins & Nelson H.F. Beebe著)
- shell腳本學(xué)習(xí)指南[三](Arnold Robbins & Nelson H.F. Beebe著)