原文:http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/index.html
目錄
摘要
本章我們會討論:
- 什么是 gawk?
- 在命令行中使用 gawk 命令
- 怎么使用 gawk 來格式化文本
- gawk 怎么使用則正則表達式
- 腳本中的 gawk
- gawk 和變量
把它變得更有趣味 |
就像sed一樣,整本書寫了多個版本的awk。這個介紹遠還不夠完整,且只是為了能夠理解在后面章節中的例子。要得到更多的信息,最好是從 GNU awk 的隨附文檔開始:“GAWK: Effective AWK Programming: A User's Guide for GNU Awk”. |
6.1. gawk上路
6.1.1. 什么是gawk?
Gawk 是通常在UNIX系統下使用的另外一個流行的流編輯器 awk 的GNU版本。盡管 awk 程序常常只是一個 gawk 的連接,但是我們還是稱作它為 awk。
awk 的最基本的作用是搜索含有1個或者多個模板的文件中的行或者其他文本塊。當一行符合搜索的patterns,在該行就會實現指定的動作。
在 awk 中的程序和許多其他語言中的程序是不一樣的,因為 awk 程序是 “數據驅動的” :你先描述你想處理的數據,然后當找到它們的時候怎么處理。許多其他的語言是“過程的”。你需要具體的描述程序該采取的措施。當使用過程化的語言時,通常更難清楚地描述你需要處理的數據。正因為如此, awk 程序經常能清爽容易地讀寫。
6.1.2. Gawk命令
當你運行 awk 的時候,你指定一個 awk 程序 來通知 awk 該如何做。程序由一系列 規則 組成。(也可能包含函數定義,循環,條件和其他程序結構,高級特性等一系列我們已經遺忘的東西。)每個規則指定了搜索的模板和在搜索到一個模板后執行的動作。
有幾種運行 awk 的方法。如果程序很短,最方便的是在命令行運行它:
awk PROGRAM inputfile(s)
在已經完成的多個改變,通常在多個文件,很方便把 awk 命令放到腳本當中,讀起來像這樣:
awk -f PROGRAM-FILE inputfile(s)
源文檔 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06.html>
6.2. 打印程序
6.2.1. 打印選擇的域
awk 中的 print 命令把輸入文件中選擇的數據輸出。
當 awk 讀取文件的一行,根據指定的 輸入域分隔符 把行分開, FS,awk 的一個環境變量(見 第 6.3.2 節 “輸出分隔符”)。它被預先定義為一個或者多個空格或者制表符。
變量 $1, $2, $3, ..., $N 把輸入行的第一第二第三直到最后一個域保存起來。變量 $0 把整行的值保存起來。在下面的圖片描述中,我們看到 df 命令輸出有6欄。
圖 6.1. awk中的塊
在 ls -l 的輸出,有9欄。 print 語句像這樣使用這些域:
kelly@octarine ~/test> ls -l | awk '{ print $5 $9 }'
160orig
121script.sed
120temp_file
126test
120twolines
441txt2html.sh
kelly@octarine ~/test>
這個命令把包含文件大小的第五欄和包含文件名的最后一欄打印出來。除非你使用正式的方法來用一個逗號來分割你想打印的列,否則輸出并不十分適合閱讀。這樣的情況下,默認的輸出分割字符,通常是一個空格,將被放入每個輸出域。
6.2.2. 格式化域
沒有格式化,只使用輸出分隔符的話看上去比較破,插入幾個制表符和指示輸出的標記會使它變得漂亮很多:
kelly@octarine ~/test> ls -ldh * | grep -v total | \
awk '{ print "Size is " $5 " bytes for " $9 }'
Size is 160 bytes for orig
Size is 121 bytes for script.sed
Size is 120 bytes for temp_file
Size is 126 bytes for test
Size is 120 bytes for twolines
Size is 441 bytes for txt2html.sh
kelly@octarine ~/test>
注意反斜杠的用法,讓shell不把它翻譯成分隔命令而使其在下一行繼續很長的輸入。雖然命令行的輸入實際上是沒有長度限制的,但是你的顯示器不是,打印的紙當然也不是。使用反斜杠也允許拷貝和粘貼以上行到一個終端窗口。
ls 的 -h 選項用來支持把大文件的字節數轉換成更容易讀的格式。當把目錄作為參數的時候長列表的輸出顯示目錄中快的總數。這行對我們并沒有什么用處,所以我們加上一個*。同樣的原因我們同樣加上 -d 選項,萬一*對一個目錄展開。
在這個例子中的反斜杠提示了一行的延長。見 第 3.3.2 節 “轉義字符”。
甚至在反序中你也能取出任何欄的的數字。下面的例子證明了最危險的區分。
kelly@octarine ~> df -h | sort -rnk 5 | head -3 | \
awk '{ print "Partition " $6 "\t: " $5 " full!" }'
Partition /var : 86% full!
Partition /usr : 85% full!
Partition /home : 70% full!
kelly@octarine ~>
下表給出了特殊格式化字符的總攬:
表 6.1. gawk的格式化字符
轉義字符 | 含義 |
\a | Bell character |
\n | Newline character |
\t | Tab |
引用,$和其他元字符應該使用反斜杠來進行轉義。
6.2.3. print命令和正則表達式
用斜杠把正則表達式包含起來可以當作一個pattern。然后正則表達式測試整個文本的每條記錄。語法如下:
awk 'EXPRESSION { PROGRAM }' file(s)
下面的例子現實了只有本地磁盤設備的信息,網絡文件系統沒有顯示:
kelly is in ~> df -h | awk '/dev\/hd/ { print $6 "\t: " $5 }'
/ : 46%
/boot : 10%
/opt : 84%
/usr : 97%
/var : 73%
/.vol1 : 8%
kelly is in ~>
斜杠也需要轉義,因為對于 awk 它們有著特殊的含義。
下面的另外一個例子是我們在 /etc 目錄搜索以 “.conf” 結尾和 “a” 或者 “x” 開頭的文件,使用擴展的正則表達式:
kelly is in /etc> ls -l | awk '/\<(a|x).*\.conf$/ { print $9 }'
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf
kelly is in /etc>
這個例子說明了在正則表達式中.的特殊意義。第一個表明了我們想要搜索在第一個搜索字符串之后的任何字符,第二個因為是要查找字符串的一部分所以被轉義了(文件名的結束)。
6.2.4. 特殊的patterns
為了在輸出之前加上注釋,使用BEGIN語句:
kelly is in /etc> ls -l | \
awk 'BEGIN { print "Files found:\n" } /\<[a|x].*\.conf$/ { print $9 }'
Files found:
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf
kelly is in /etc>
加上 END 語句能插入文本在整個輸入被處理之后:
kelly is in /etc> ls -l | \
awk '/\<[a|x].*\.conf$/ { print $9 } END { print \
"Can I do anything else for you, mistress?" }'
amd.conf
antivir.conf
xcdroast.conf
xinetd.conf
Can I do anything else for you, mistress?
kelly is in /etc>
6.2.5. Gawk腳本
由于往往命令都有點長,你可能想把他們放到腳本中,來重用它。一個 awk 腳本包含定義pattern和動作的awk語句。
作為一個說明,我們將建立一個報告來顯示占用率最高的分區,參見 第 6.2.2 節 “格式化域”。
kelly is in ~> cat diskrep.awk
BEGIN { print "*** WARNING WARNING WARNING ***" }
/\<[8|9][0-9]%/ { print "Partition " $6 "\t: " $5 " full!" }
END { print "*** Give money for new disks URGENTLY! ***" }
kelly is in ~> df -h | awk -f diskrep.awk
*** WARNING WARNING WARNING ***
Partition /usr : 97% full!
*** Give money for new disks URGENTLY! ***
kelly is in ~>
awk 先打印一個開始信息,然后格式化所有包含一個8或者9開頭的詞的行,然后后接一個數字和百分符號,然后加入結束信息。
語法高亮 |
Awk是一個編程語言。他的語法能被大多數能對其他語言比如c,bash,HTML進行語法高亮編輯器所識別。 |
源文檔 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06s02.html>
6.3. Gawk變量
既然 awk 處理輸入文件,他使用幾個變量。一些是可以編輯的,一些是只讀的。
6.3.1. 輸入域的分隔符
域分隔符,既不是一個單獨的字符也不是一個普通的表達式,是控制 awk 把一個輸入分割成幾個域。輸入記錄按分割定義進行字符順序掃描;域就是在相符的那些文字中間的那部分。
域分隔符代表內建的變量 FS,注意POSIX標準的shell使用的變量 IFS 和他是有一些區別的。
域分隔符變量的值可以在 awk 程序中用賦值操作符 = 來改變。通常最好的執行時間是一開始也就是還沒有處理任何輸入的時候,因此第一個記錄就被隨合適的分隔符一起讀取。要這么做,清使用特殊的 BEGIN pattern。
以下的例子,我們編制了一條命令來顯示系統里的所有用戶及其描述:
kelly is in ~> awk 'BEGIN { FS=":" } { print $1 "\t" $5 }' /etc/passwd
--output omitted--
kelly Kelly Smith
franky Franky B.
eddy Eddy White
willy William Black
cathy Catherine the Great
sandy Sandy Li Wong
kelly is in ~>
在一個 awk 腳本中,它看起來像這樣:
kelly is in ~> cat printnames.awk
BEGIN { FS=":" }
{ print $1 "\t" $5 }
kelly is in ~> awk -f printnames.awk /etc/passwd
--output omitted--
小心地選擇輸入分隔域來防止出現問題。一個例子來說明這個:說你需要輸入想這樣的行的形式:
“Sandy L. Wong, 64 Zoo St., Antwerp, 2000X”
你這樣寫了一個打印出記錄中人的名字的命令行或者是腳本:
awk 'BEGIN { FS="," } { print $1, $2, $3 }' inputfile
但是可能一個人有PhD,而且可能寫成這樣:
“Sandy L. Wong, PhD, 64 Zoo St., Antwerp, 2000X”
你的 awk 會給出錯誤的輸出。需要的話,使用額外的一個 awk 或者 sed 來統一數據輸出的格式。
默認的輸入分隔符是一個或多個空格和制表符。
6.3.2. 輸出分隔符
6.3.2.1. 輸出域分隔符
在輸出中域通常被空格分隔。當你對 print 命令使用正確的語法且字段用逗號分隔時,這將很明顯的:
kelly@octarine ~/test> cat test
record1 data1
record2 data2
kelly@octarine ~/test> awk '{ print $1 $2}' test
record1data1
record2data2
kelly@octarine ~/test> awk '{ print $1, $2}' test
record1 data1
record2 data2
kelly@octarine ~/test>
如果你不輸入逗號, print 將把輸出的項目全部當成一個字段,因此省略默認輸出 分隔符, OFS的使用。
在攝制了這個內建變量的值之后任何字符串可以被用作一個輸出域。
6.3.2.2. 輸出記錄分隔符
整個 print 語句的輸出叫做輸出記錄。每個在輸出記錄里的print命令的結果,輸出一個叫做 輸出記錄分隔符 ORS的字符串。這個變量的默認值是 “\n”,一個換行符。因此,每個 print 語句生成一個單獨的行。
要改變輸出域和記錄的,只要給 OFS 和 ORS 賦新的值:
kelly@octarine ~/test> awk 'BEGIN { OFS=";" ; ORS="\n-->\n" } \
{ print $1,$2}' test
record1;data1
-->
record2;data2
-->
kelly@octarine ~/test>
如果 ORS 的值不包含換行,那么程序中的輸出就會輸出成一個單獨行。
6.3.3. 記錄的數量
內建的 NR 包含了處理過的記錄的數量。在讀入一個新的輸入行之后他會自行增加一次。你可以用它來計算記錄的總數,或者在每個輸出記錄中:
kelly@octarine ~/test> cat processed.awk
BEGIN { OFS="-" ; ORS="\n--> done\n" }
{ print "Record number " NR ":\t" $1,$2 }
END { print "Number of records processed: " NR }
kelly@octarine ~/test> awk -f processed.awk test
Record number 1: record1-data1
--> done
Record number 2: record2-data2
--> done
Number of records processed: 2
--> done
kelly@octarine ~/test>
6.3.4. 用戶定義的變量
除了內建的變量之外,你也可以定義自己的變量。當 awk 碰到一個不存在的變量(沒有事先定義的)的引用時,這個變量就被創建并且使用一個空字符串進行賦值。對于后來所有的引用,就是該變量最后被賦予的那個值。變量可以是一個字符串或者一個數字。輸入域的內容也可以被賦予變量。
值可以直接用 = 來賦值,或者你可以使用現有變量的值和其他操作符組合:
kelly@octarine ~> cat revenues
20021009 20021013 consultancy BigComp 2500
20021015 20021020 training EduComp 2000
20021112 20021123 appdev SmartComp 10000
20021204 20021215 training EduComp 5000
kelly@octarine ~> cat total.awk
{ total=total + $5 }
{ print "Send bill for " $5 " dollar to " $4 }
END { print "---------------------------------\nTotal revenue: " total }
kelly@octarine ~> awk -f total.awk test
Send bill for 2500 dollar to BigComp
Send bill for 2000 dollar to EduComp
Send bill for 10000 dollar to SmartComp
Send bill for 5000 dollar to EduComp
---------------------------------
Total revenue: 19500
kelly@octarine ~>
類似于C的簡寫 VAR+= value 也是可以接受的。
6.3.5. 更多例子
當我們使用 awk 腳本時, 第 5.3.2 節 “寫輸出文件” 的例子會變得更加容易:
kelly@octarine ~/html> cat make-html-from-text.awk
BEGIN { print "<html>\n<head><title>Awk-generated HTML</title></head>\n<body bgcolor=\"#ffffff\">\n<pre>" }
{ print $0 }
END { print "</pre>\n</body>\n</html>" }
而且當用 awk 來替代 sed 后,命令也變得更加直截了當:
kelly@octarine ~/html> awk -f make-html-from-text.awk testfile > file.html
在你系統上的awk例子。 |
我們再次回到包含你系統啟動腳本的目錄。輸入一個和以下相似的命令來查看更多 awk 命令的使用方法: grep awk /etc/init.d/* |
6.3.6. printf程序
為了更精確的控制 print 提供的正常輸出格式,可以使用 printf。 printf命令可以用來指明每個項目使用的域的寬度,同時也有用于數字的多種格式選擇。這一切只要增加一個字符串,叫做格式字符串,控制怎樣和那里來打印那些項目。
語法也和C語言的 printf 語句相似;請察看你的C介紹手冊。 gawk 信息頁面包含完整的解釋。
源文檔 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06s03.html>
6.4. 總結
gawk 工具解釋特殊目的的編程語言,處理簡單的數據重新格式化的工作,只需要幾行代碼。他是通常UNIX awk 命令的免費版本。
這個工具從輸入數據讀取行且能夠方便的識別。 print 是最普通的來過濾和格式化定義的域的程序。
閑置的變量可以直接聲明也允許在處理輸入流的同時進行簡單的計算求和,統計和其他運算。變量和命令可以放到 awk 腳本來進行后臺處理。
源文檔 <http://savs.hcc.edu.tw/~chuavv/bash/Bash-Beginners-Guide-SimplifiedChinese/ch06s04.html>