亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

awk 實例

2024-06-28 13:26:07
字體:
來源:轉載
供稿:網友
awk 實例

這個來自于:http://blog.ithomer.net/2013/11/awk-example-1-variable/

三個實例文章,我集合成一篇文章,方便自己以后查詢使用


awk 實例(1)—— 變量

簡介: Awk 是一種名稱奇怪但功能強大的語言。本文是一個包含三部分的系列的第一篇。在本文中,DanielRobbins 將使您迅速掌握 awk 編程技巧。隨著本系列的進展,將討論更高級的主題,最后將演示一個真實的高級 awk 應用程序。

捍衛 awk

在這一系列的文章中,我將使您成為精通 awk 的編程人員。我承認,awk 并沒有一個非常好聽且又非常 “時髦” 的名字。awk 的 GNU 版本(叫作 gawk)聽起來非常怪異。那些不熟悉這種語言的人可能聽說過 “awk”,而且可能認為它是一組落伍且過時的混亂代碼。它甚至會使最博學的 UNIX 權威陷于混亂的邊緣(使他不斷地發出 “kill -9!” 命令,就象使用咖啡機一樣)。

的確,awk 沒有一個動聽的名字。但它是一種很棒的語言。awk 適合于文本處理和報表生成,它還有許多精心設計的特性,允許進行多種方式的編程。與某些語言不同,awk 的語法較為常見。它借鑒了某些語言的一些精華部分,如 C 語言、python 和 bash(雖然在技術上,awk 比 python 和 bash 早創建)。awk 是一種一旦學會就會成為您戰略代碼庫的重要部分的語言。

第一個 Awk

您應該會看到 /ect/passwd 文件中的內容,本文使用該文件來解釋 awk 的工作原理。當調用 awk 時,我們指定 /etc/passwd 作為輸入文件。Awk 在執行期間對 /etc/passwd 文件中的每一行依次執行 PRint 命令。所有輸出都發送到 stdout,可以得到類似 cat 命令的結果。

現在解釋代碼塊 { print }。在 Awk 中,花括號用于將代碼分塊,這與 C 語言類似。我們的代碼塊中只有一條 print 命令。在 Awk 中,當 print 命令單獨出現時,將打印當前行的全部內容。

$ awk '{ print $0 }' /etc/passwd

在 Awk 中,變量 $0 表示整個當前行,因此 printprint $0 的作用完全相同。

$ awk '{ print "" }' /etc/passwd
$ awk '{ print "hiya" }' /etc/passwd

運行該腳本,屏幕上講顯示多行 hiya。

多個字段

print $1

$ awk -F":" '{ print $1 $3 }' /etc/passwd
halt7Operator11root0shutdown6sync5bin1....etc.

print $1 $3

$ awk -F":" '{ print $1 " " $3 }' /etc/passwd

$1 $3

$ awk -F":" '{ print "username: " $1 "/t/tuid:" $3 }' /etc/passwd
username: halt          uid:7username: operator      uid:11username: root          uid:0username: shutdown      uid:6username: sync          uid:5username: bin           uid:1....etc.

外部腳本

BEGIN {FS=":"}{ print $1 }

兩種方法的區別在于如何設置字段分隔符。在該腳本中,(通過設置 FS 變量)在代碼中指定字段分隔符,而前一示例通過在命令行向 awk 傳遞 -F”:” 選項來設置 FS。一般而言,最好在腳本內部設置字段分隔符,因為這樣可以少輸入一個命令行參數。本文稍后將深入講解 FS 變量。

BEGIN 與 END 代碼塊

通常,awk 會針對每個輸入行執行一次每個代碼塊。但是,在許多編程情形下,可能需要在 awk 開始處理輸入文件的文本之前 執行初始化代碼。對這種情況,awk 支持定義 BEGIN 代碼塊。前一示例使用了這種代碼塊。因為 BEGIN 代碼塊在 awk 開始處理輸入文件之前執行,因此它是初始化 FS(字段分隔符)變量、打印頁眉或者初始化在后續程序中將要引用的其他全局變量的絕佳位置。

另外,awk 還提供了另一種稱為 END 的專用代碼塊。在輸入文件的所有行處理完畢之后,awk 執行這個代碼塊。通常,END 代碼塊用于進行最終計算或者打印應該在輸出流結尾處出現的匯總信息。

正則表達式與代碼塊

/foo/ { print }
/[0-9]+/.[0-9]*/ { print }

表達式與代碼塊

fred print

$1 == "fred" { print $3 }

root

$5 ~ /root/ { print $3 }

條件語句

if

{ if ( $5 ~ /root/ ) { print $3 }}

兩個腳本的作用相同。第一個示例的布爾表達式位于代碼塊外,而第二個示例的代碼塊會針對每個輸入行執行一次,本文使用 if 語句有選擇地執行打印命令。兩種方法都可以使用,可以選擇與腳本的其他部分最匹配的方法。

if 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 }
{ if ( $0 !~ /matchme/ ) {print $1 $3 $4}}

兩個腳本都會只輸出 包含 matchme 字符序列的行。也可以選擇最適合您的代碼的方法。它們的功能完全相同。

( $1 == "foo" ) && ( $2 == "bar" ) { print } 

該示例只打印第一個字段等于 foo 第二字段等于 bar 的行。

數值變量

在 BEGIN 代碼塊中,我們將整型變量 x 初始化為零。這樣,awk 每次遇到空白行時都將執行 x=x+1 語句,遞增 x 值。在所有行都處理完畢之后,awk 執行 END 代碼塊,并打印最終的匯總信息,以顯示它找到的空白行數。

字符串化變量

2.01

1.01 x $( ) 1.01

{ print ($1^2)+1 }

稍做試驗就可以發現,如果特定變量不包含效數字,那么 awk 在計算數學表達式時將該變量當作數值零處理。

眾多運算符

Awk 的另一個優點是它擁有全面的數學運算符。除了標準的加減乘除,awk 還支持前面演示的指數運算符 “^”、求模(余數)運算符 “%” 和借鑒自 C 語言的大量方便的賦值運算符。

其中包括前后加/減(i++、--foo), 加減乘除賦值運算符(a+=3、b*=2、c/=2.2d-=6.2)。而且,這僅僅是一部分 —— 我們還能使用方便的求模/指數賦值運算符(a^=2、b%=4)。

字段分隔符

Awk 有其自己的特殊變量集合。其中一些變量支持調優 awk 性能,而且可以讀取另一些變量來收集關于輸入的重要信息。前面已經接觸過特殊變量 FS。如前所述,這個變量支持設置 awk 期望在字段中找到的字符序列。當我們使用 /ect/passwd 作為輸入時,FS 設為 “:”。盡管這樣做可以解決問題,但 FS 還支持更高的靈活性。

FS="/t+"

上面使用特殊的正則表達式字符 “+”,表示 “一個或多個前一字符”。

FS="[[:space:]+]"

盡管該賦值運算符能夠解決問題,但是并非必要。為什么呢?因為在默認情況下,FS 被設為單個空格字符,awk 將其解釋為 “一個或多個空格或制表符”。在這個特定的示例中,默認的 FS 設置恰好是您最想要的設置。

FS="foo[0-9][0-9][0-9]"

字段數目

{if ( NF > 2 ) {print $1 " " $2 ":" $3 }}

記錄數目

{#skip headerif ( NR > 10 ) {print "ok, now for the real information!"}}

Awk 還提供了一些具有多種用途的其他變量。后續文章中將深入講解這些變量。

我們對 awk 的初次探究現在就結束了。隨著本系列的延續,我將演示更高級的 awk 功能,我們將用一個真實的 awk 應用程序作為本系列的結尾。


awk 實例(2)—— 功能

簡介: 在這篇 awk簡介的續集中,Daniel Robbins 繼續探索awk(一種很棒但有怪異名稱的語言)。Daniel將演示如何處理多行記錄、使用循環結構,以及創建并使用 awk數組。閱讀完本文后,您將精通許多 awk的功能,而且可以編寫您自己的功能強大的 awk 腳本。

多行記錄

awk 是一種用于讀取和處理結構化數據(如系統的 /etc/passwd 文件)的極佳工具。/etc/passwd 是 UNIX 用戶數據庫,并且是用冒號定界的文本文件,它包含許多重要信息,包括所有現有用戶帳戶和用戶標識,以及其它信息。在我的 前一篇文章 中,我演示了 awk 如何輕松地分析這個文件。我們只須將 FS(字段分隔符)變量設置成 “:”。

正確設置了 FS 變量之后,就可以將 awk 配置成分析幾乎任何類型的結構化數據,只要這些數據是每行一個記錄。然而,如果要分析占據多行的記錄,僅僅依靠設置 FS 是不夠的。在這些情況下,我們還需要修改 RS 記錄分隔符變量。RS 變量告訴 awk 當前記錄什么時候結束,新記錄什么時候開始。

譬如,讓我們討論一下如何完成處理“聯邦證人保護計劃”所涉及人員的地址列表的任務:

Jimmy the Weasel100 Pleasant DriveSan Francisco, CA 12345Big Tony200 Incognito Ave.Suburbia, WA 67890

理論上,我們希望 awk 將每 3 行看作是一個獨立的記錄,而不是三個獨立的記錄。如果 awk 將地址的第一行看作是第一個字段 ($1),街道地址看作是第二個字段 ($2),城市、州和郵政編碼看作是第三個字段 $3,那么這個代碼就會變得很簡單。以下就是我們想要得到的代碼:

BEGIN {    FS="/n"    RS=""}

在上面這段代碼中,將 FS 設置成 “/n” 告訴 awk 每個字段都占據一行。通過將 RS 設置成 “”,還會告訴 awk 每個地址記錄都由空白行分隔。一旦 awk 知道是如何格式化輸入的,它就可以為我們執行所有分析工作,腳本的其余部分很簡單。讓我們研究一個完整的腳本,它將分析這個地址列表,并將每個記錄打印在一行上,用逗號分隔每個字段。

address.awk

BEGIN {    FS="/n"    RS=""}{    print $1 ", " $2 ", " $3}

如果這個腳本保存為 address.awk,地址數據存儲在文件 address.txt 中,可以通過輸入 “awk -f address.awk address.txt” 來執行這個腳本。此代碼將產生以下輸出:

Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345Big Tony, 200 Incognito Ave., Suburbia, WA 67890

OFS 和 ORS

在 address.awk 的 print 語句中,可以看到 awk 會連接(合并)一行中彼此相鄰的字符串。我們使用此功能在同一行上的三個字段之間插入一個逗號和空格 (“, “)。這個方法雖然有用,但比較難看。與其在字段間插入 “, ” 字符串,倒不如讓通過設置一個特殊 awk 變量 OFS,讓 awk 完成這件事。請參考下面這個代碼片斷。

print "Hello", "there", "Jim!"

這行代碼中的逗號并不是實際文字字符串的一部分。事實上,它們告訴 awk “Hello”、”there” 和 “Jim!” 是單獨的字段,并且應該在每個字符串之間打印 OFS 變量。缺省情況下,awk 產生以下輸出:

Hello there Jim!

這是缺省情況下的輸出結果,OFS 被設置成 ” “,單個空格。不過,我們可以方便地重新定義 OFS,這樣 awk 將插入我們中意的字段分隔符。以下是原始 address.awk 程序的修訂版,它使用 OFS 來輸出那些中間的 “, ” 字符串:

address.awk 的修訂版

BEGIN {    FS="/n"    RS=""    OFS=", "}{    print $1, $2, $3}

awk 還有一個特殊變量 ORS,全稱是“輸出記錄分隔符”。通過設置缺省為換行 (“/n”) 的 OFS,我們可以控制在 print 語句結尾自動打印的字符。缺省 ORS 值會使 awk 在新行中輸出每個新的 print 語句。如果想使輸出的間隔翻倍,可以將 ORS 設置成 “/n/n”?;蛘撸绻胍脝蝹€空格分隔記錄(而不換行),將 ORS 設置成 “”。

將多行轉換成用 tab 分隔的格式

假設我們編寫了一個腳本,它將地址列表轉換成每個記錄一行,且用 tab 定界的格式,以便導入電子表格。使用稍加修改的 address.awk 之后,就可以清楚地看到這個程序只適合于三行的地址。如果 awk 遇到以下地址,將丟掉第四行,并且不打印該行:

Cousin VinnieVinnie's Auto Shop300 City AlleySosueme, OR 76543

要處理這種情況,代碼最好考慮每個字段的記錄數量,并依次打印每個記錄?,F在,代碼只打印地址的前三個字段。以下就是我們想要的一些代碼:

適合具有任意多字段的地址的 address.awk 版本

BEGIN {     FS="/n"     RS=""     ORS="" }  {          x=1         while ( x<NF ) {                 print $x "/t"                 x++         }         print $NF "/n" } 

首先,將字段分隔符 FS 設置成 “/n”,將記錄分隔符 RS 設置成 “”,這樣 awk 可以象以前一樣正確分析多行地址。然后,將輸出記錄分隔符 ORS 設置成 “”,它將使 print 語句在每個調用結尾 輸出新行。這意味著如果希望任何文本從新的一行開始,那么需要明確寫入 print "/n" 。

在主代碼塊中,創建了一個變量 x 來存儲正在處理的當前字段的編號。起初,它被設置成 1。然后,我們使用 while 循環(一種 awk 循環結構,等同于 C 語言中的 while 循環),對于所有記錄(最后一個記錄除外)重復打印記錄和 tab 字符。最后,打印最后一個記錄和換行;此外,由于將 ORS 設置成 “”,print 將不輸出換行。程序輸出如下,這正是我們所期望的:

我們想要的輸出。不算漂亮,但用 tab 定界,以便于導入電子表格

Jimmy the Weasel        100 Pleasant Drive      San Francisco, CA 12345 Big Tony        200 Incognito Ave.      Suburbia, WA 67890Cousin Vinnie   Vinnie's Auto Shop      300 City Alley  Sosueme, OR 76543

循環結構

我們已經看到了 awk 的 while 循環結構,它等同于相應的 C 語言 while 循環。awk 還有 “do…while” 循環,它在代碼塊結尾處對條件求值,而不象標準 while 循環那樣在開始處求值。它類似于其它語言中的 “repeat…until” 循環。以下是一個示例:

do…while 示例

{    count=1    do {        print "I get printed at least once no matter what"     } while ( count != 1 )}

與一般的 while 循環不同,由于在代碼塊之后對條件求值,”do…while” 循環永遠都至少執行一次。換句話說,當第一次遇到普通 while 循環時,如果條件為假,將永遠不執行該循環。

for 循環 awk 允許創建 for 循環,它就象 while 循環,也等同于 C 語言的 for 循環:

for ( initial assignment; comparison; increment ) {    code block}

以下是一個簡短示例:

for ( x = 1; x <= 4; x++ ) {    print "iteration",x}

此段代碼將打印:

iteration 1iteration 2iteration 3iteration 4

break 和 continue

此外,如同 C 語言一樣,awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制 awk 的循環結構。以下是迫切需要 break 語句的代碼片斷:

while 死循環

while (1) {    print "forever and ever..."}

while 死循環 1 永遠代表是真,這個 while 循環將永遠運行下去。以下是一個只執行十次的循環:

break 語句示例

x=1while(1) {    print "iteration",x    if ( x == 10 ) {        break    }    x++}

這里,break 語句用于“逃出”最深層的循環。”break” 使循環立即終止,并繼續執行循環代碼塊后面的語句。

continue 語句補充了 break,其作用如下:

x=1while (1) {    if ( x == 4 ) {        x++        continue    }    print "iteration",x    if ( x > 20 ) {        break    }    x++}

這段代碼打印 “iteration 1″ 到 “iteration 21″,”iteration 4″ 除外。如果迭代等于 4,則增加 x 并調用 continue 語句,該語句立即使 awk 開始執行下一個循環迭代,而不執行代碼塊的其余部分。如同 break 一樣,continue 語句適合各種 awk 迭代循環。在 for 循環主體中使用時,continue 將使循環控制變量自動增加。以下是一個等價循環:

for ( x=1; x<=21; x++ ) {    if ( x == 4 ) {        continue    }    print "iteration",x}

在 while 循環中時,在調用 continue 之前沒有必要增加 x ,因為 for 循環會自動增加 x 。

數組

如果您知道 awk 可以使用數組,您一定會感到高興。然而,在 awk 中,數組下標通常從 1 開始,而不是 0:

myarray[1]="jim"myarray[2]=456

awk 遇到第一個賦值語句時,它將創建 myarray ,并將元素 myarray[1] 設置成 “jim”。執行了第二個賦值語句后,數組就有兩個元素了。

數組迭代 定義之后,awk 有一個便利的機制來迭代數組元素,如下所示:

for ( x in myarray ) {    print myarray[x]}

這段代碼將打印數組 myarray 中的每一個元素。當對于 for 使用這種特殊的 “in” 形式時,awk 將 myarray 的每個現有下標依次賦值給x (循環控制變量),每次賦值以后都循環一次循環代碼。雖然這是一個非常方便的 awk 功能,但它有一個缺點 — 當 awk 在數組下標之間輪轉時,它不會依照任何特定的順序。那就意味著我們不能知道以上代碼的輸出是:

jim456

還是

456jim

套用 Forrest Gump 的話來說,迭代數組內容就像一盒巧克力 — 您永遠不知道將會得到什么。因此有必要使 awk 數組“字符串化”,我們現在就來研究這個問題。

數組下標字符串化

在我的 前一篇文章中,我演示了 awk 實際上以字符串格式來存儲數字值。雖然 awk 要執行必要的轉換來完成這項工作,但它卻可以使用某些看起來很奇怪的代碼:

a="1"b="2"c=a+b+3

執行了這段代碼后, c 等于 6 。由于 awk 是“字符串化”的,添加字符串 “1″ 和 “2″ 在功能上并不比添加數字 1 和 2 難。這兩種情況下,awk 都可以成功執行運算。awk 的“字符串化”性質非??蓯?— 您可能想要知道如果使用數組的字符串下標會發生什么情況。例如,使用以下代碼:

myarr["1"]="Mr. Whipple"print myarr["1"]

可以預料,這段代碼將打印 “Mr. Whipple”。但如果去掉第二個 “1″ 下標中的引號,情況又會怎樣呢?

myarr["1"]="Mr. Whipple"print myarr[1]

猜想這個代碼片斷的結果比較難。awk 將 myarr["1"]myarr[1] 看作數組的兩個獨立元素,還是它們是指同一個元素?答案是它們指的是同一個元素,awk 將打印 “Mr. Whipple”,如同第一個代碼片斷一樣。雖然看上去可能有點怪,但 awk 在幕后卻一直使用數組的字符串下標!

了解了這個奇怪的真相之后,我們中的一些人可能想要執行類似于以下的古怪代碼:

myarr["name"]="Mr. Whipple"print myarr["name"]

這段代碼不僅不會產生錯誤,而且它的功能與前面的示例完全相同,也將打印 “Mr. Whipple”!可以看到,awk 并沒有限制我們使用純整數下標;如果我們愿意,可以使用字符串下標,而且不會產生任何問題。只要我們使用非整數數組下標,如 myarr["name"] ,那么我們就在使用 關聯數組 。從技術上講,如果我們使用字符串下標,awk 的后臺操作并沒有什么不同(因為即便使用“整數”下標,awk 還是會將它看作是字符串)。但是,應該將它們稱作 關聯數組 – 它聽起來很酷,而且會給您的上司留下印象。字符串化下標是我們的小秘密。;)

數組工具

談到數組時,awk 給予我們許多靈活性??梢允褂米址聵?,而且不需要連續的數字序列下標(例如,可以定義 myarr[1]myarr[1000] ,但不定義其它所有元素)。雖然這些都很有用,但在某些情況下,會產生混淆。幸好,awk 提供了一些實用功能有助于使數組變得更易于管理。

首先,可以刪除數組元素。如果想要刪除數組 fooarray 的元素 1 ,輸入:

delete fooarray[1]

而且,如果想要查看是否存在某個特定數組元素,可以使用特殊的 “in” 布爾運算符,如下所示:

if ( 1 in fooarray ) { print "Ayep! It's there." } else { print "Nope! Can't find it." }

awk 實例(3)—— 函數

簡介: 在這篇 awk 系列的總結中,Daniel 向您介紹 awk 重要的字符串函數,以及演示了如何從頭開始編寫完整的支票簿結算程序。在這個過程中,您將學習如何編寫自己的函數,并使用 awk 的多維數組。學完本文之后,您將掌握更多 awk 經驗,可以讓您創建功能更強大的腳本。

格式化輸出

雖然大多數情況下 awk 的 print 語句可以完成任務,但有時我們還需要更多。在那些情況下,awk 提供了兩個我們熟知的老朋友 printf() 和 sprintf()。是的,如同其它許多 awk 部件一樣,這些函數等同于相應的 C 語言函數。printf() 會將格式化字符串打印到 stdout,而 sprintf() 則返回可以賦值給變量的格式化字符串。如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速了解這兩個基本打印函數。在 linux 系統上,可以輸入 “man 3 printf” 來查看 printf() 幫助頁面。

以下是一些 awk sprintf() 和 printf() 的樣本代碼??梢钥吹?,它們幾乎與 C 語言完全相同。

x=1b="foo"printf("%s got a %d on the last test/n","Jim",83)myout=("%s-%d",b,x)print myout

此代碼將打?。?/p>

Jim got a 83 on the last testfoo-1

字符串函數

awk 有許多字符串函數,這是件好事。在 awk 中,確實需要字符串函數,因為不能象在其它語言(如 C、C++ 和 Python)中那樣將字符串看作是字符數組。例如,如果執行以下代碼:

mystring="How are you doing today?"print mystring[3]

將會接收到一個錯誤,如下所示:

awk: string.gawk:59: fatal: attempt to use scalar as array

噢,好吧。雖然不象 Python 的序列類型那樣方便,但 awk 的字符串函數還是可以完成任務。讓我們來看一下。

首先,有一個基本 length() 函數,它返回字符串的長度。以下是它的使用方法:

print length(mystring)

此代碼將打印值:

24

好,繼續。下一個字符串函數叫作 index,它將返回子字符串在另一個字符串中出現的位置,如果沒有找到該字符串則返回 0。使用 mystring,可以按以下方法調用它:

print index(mystring,"you")

awk 會打?。?/p>

9

讓我們繼續討論另外兩個簡單的函數,tolower() 和 toupper()。與您猜想的一樣,這兩個函數將返回字符串并且將所有字符分別轉換成小寫或大寫。請注意,tolower() 和 toupper() 返回新的字符串,不會修改原來的字符串。這段代碼:

print tolower(mystring)print toupper(mystring)print mystring

……將產生以下輸出:

how are you doing today?HOW ARE YOU DOING TODAY?How are you doing today?

到現在為止一切不錯,但我們究竟如何從字符串中選擇子串,甚至單個字符?那就是使用 substr() 的原因。以下是 substr() 的調用方法:

mysub=substr(mystring,startpos,maxlen)

mystring 應該是要從中抽取子串的字符串變量或文字字符串。startpos 應該設置成起始字符位置,maxlen 應該包含要抽取的字符串的最大長度。請注意,我說的是 最大長度 ;如果 length(mystring) 比 startpos+maxlen 短,那么得到的結果就會被截斷。substr() 不會修改原始字符串,而是返回子串。以下是一個示例:

print substr(mystring,9,3)

awk 將打?。?/p>

you

如果您通常用于編程的語言使用數組下標訪問部分字符串(以及不使用這種語言的人),請記住 substr() 是 awk 代替方法。需要使用它來抽取單個字符和子串;因為 awk 是基于字符串的語言,所以會經常用到它。

現在,我們討論一些更耐人尋味的函數,首先是 match()。match() 與 index() 非常相似,它與 index() 的區別在于它并不搜索子串,它搜索的是規則表達式。match() 函數將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match() 還將設置兩個變量,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH 指定它占據的字符跨度(如果沒有找到匹配,則返回 -1)。通過使用 RSTART、RLENGTH、substr() 和一個小循環,可以輕松地迭代字符串中的每個匹配。以下是一個 match() 調用示例:

print match(mystring,/you/), RSTART, RLENGTH

awk 將打印:

9 9 3

字符串替換

現在,我們將研究兩個字符串替換函數,sub() 和 gsub()。這些函數與目前已經討論過的函數略有不同,因為它們 確實修改原始字符串 。以下是一個模板,顯示了如何調用 sub():

sub(regexp,replstring,mystring)

調用 sub() 時,它將在 mystring 中匹配 regexp 的第一個字符序列,并且用 replstring 替換該序列。sub() 和 gsub() 用相同的自變量;唯一的區別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub() 將執行全局替換,換出字符串中的所有匹配。以下是一個 sub() 和 gsub() 調用示例:

sub(/o/,"O",mystring)print mystringmystring="How are you doing today?"gsub(/o/,"O",mystring)print mystring

必須將 mystring 復位成其初始值,因為第一個 sub() 調用直接修改了 mystring。在執行時,此代碼將使 awk 輸出:

HOw are you doing today?HOw are yOu dOing tOday?

當然,也可以是更復雜的規則表達式。我把測試一些復雜規則表達式的任務留給您來完成。

通過介紹函數 split(),我們來匯總一下已討論過的函數。split() 的任務是“切開”字符串,并將各部分放到使用整數下標的數組中。以下是一個 split() 調用示例:

numelements=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",mymonths,",")

調用 split() 時,第一個自變量包含要切開文字字符串或字符串變量。在第二個自變量中,應該指定 split() 將填入片段部分的數組名稱。在第三個元素中,指定用于切開字符串的分隔符。split() 返回時,它將返回分割的字符串元素的數量。split() 將每一個片段賦值給下標從 1 開始的數組,因此以下代碼:

print mymonths[1],mymonths[numelements]

……將打?。?/p>

Jan Dec

特殊字符串形式

簡短注釋 — 調用 length()、sub() 或 gsub() 時,可以去掉最后一個自變量,這樣 awk 將對 $0(整個當前行)應用函數調用。要打印文件中每一行的長度,使用以下 awk 腳本:

{    print length() }

財務上的趣事

幾星期前,我決定用 awk 編寫自己的支票簿結算程序。我決定使用簡單的 tab 定界文本文件,以便于輸入最近的存款和提款記錄。其思路是將這個數據交給 awk 腳本,該腳本會自動合計所有金額,并告訴我余額。以下是我決定如何將所有交易記錄到 “ASCII checkbook” 中:

23 Aug 2000 food    -   -   Y   Jimmy's Buffet      30.25

此文件中的每個字段都由一個或多個 tab 分隔。在日期(字段 1,$1)之后,有兩個字段叫做“費用分類帳”和“收入分類帳”。以上面這行為例,輸入費用時,我在費用字段中放入四個字母的別名,在收入字段中放入 “-”(空白項)。這表示這一特定項是“食品費用”。:) 以下是存款的示例:

23 Aug 2000 -   inco    -   Y   Boss Man        2001.00

在這個實例中,我在費用分類帳中放入 “-”(空白),在收入分類帳中放入 “inco”?!眎nco” 是一般(薪水之類)收入的別名。使用分類帳別名讓我可以按類別生成收入和費用的明細分類帳。至于記錄的其余部分,其它所有字段都是不需加以說明的?!笆欠窀肚??”字段(”Y” 或 “N”)記錄了交易是否已過帳到我的帳戶;除此之外,還有一個交易描述,和一個正的美元金額。

用于計算當前余額的算法不太難。awk 只需要依次讀取每一行。如果列出了費用分類帳,但沒有收入分類帳(為 “-”),那么這一項就是借方。如果列出了收入分類帳,但沒有費用分類帳(為 “-”),那么這一項就是貸方。而且,如果同時列出了費用和收入分類帳,那么這個金額就是“分類帳轉帳”;即,從費用分類帳減去美元金額,并將此金額添加到收入分類帳。此外,所有這些分類帳都是虛擬的,但對于跟蹤收入和支出以及預算卻非常有用。

代碼

現在該研究代碼了。我們將從第一行(BEGIN 塊和函數定義)開始:

balance,第 1 部分

#!/usr/bin/env awk -fBEGIN {     FS="/t+"    months="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"}function monthdigit(mymonth) {    return (index(months,mymonth)+3)/4}

首先執行 “chmod +x myscript” 命令,那么將第一行 “#!…” 添加到任何 awk 腳本將使它可以直接從 shell 中執行。其余行定義了 BEGIN 塊,在 awk 開始處理支票簿文件之前將執行這個代碼塊。我們將 FS(字段分隔符)設置成 “/t+”,它會告訴 awk 字段由一個或多個 tab 分隔。另外,我們定義了字符串 months,下面將出現的 monthdigit() 函數將使用它。

最后三行顯示了如何定義自己的 awk 。格式很簡單 — 輸入 “function”,再輸入名稱,然后在括號中輸入由逗號分隔的參數。在此之后,”{ }” 代碼塊包含了您希望這個函數執行的代碼。所有函數都可以訪問全局變量(如 months 變量)。另外,awk 提供了 “return” 語句,它允許函數返回一個值,并執行類似于 C 和其它語言中 “return” 的操作。這個特定函數將以 3 個字母字符串格式表示的月份名稱轉換成等價的數值。例如,以下代碼:

print monthdigit("Mar")

……將打印:

3

現在,讓我們討論其它一些函數。

財務函數

以下是其它三個執行簿記的函數。我們即將見到的主代碼塊將調用這些函數之一,按順序處理支票簿文件的每一行,從而將相應交易記錄到 awk 數組中。有三種基本交易,貸方 (doincome)、借方 (doexpense) 和轉帳 (dotransfer)。您會發現這三個函數全都接受一個自變量,叫作 mybalance。mybalance 是二維數組的一個占位符,我們將它作為自變量進行傳遞。目前,我們還沒有處理過二維數組;但是,在下面可以看到,語法非常簡單。只須用逗號分隔每一維就行了。

我們將按以下方式將信息記錄到 “mybalance” 中。數組的第一維從 0 到 12,用于指定月份,0 代表全年。第二維是四個字母的分類帳,如 “food” 或 “inco”;這是我們處理的真實分類帳。因此,要查找全年食品分類帳的余額,應查看 mybalance[0,"food"]。要查找 6 月的收入,應查看 mybalance[6,"inco"]。

balance,第 2 部分

function doincome(mybalance) {    mybalance[curmonth,$3] += amount    mybalance[0,$3] += amount}function doexpense(mybalance) {    mybalance[curmonth,$2] -= amount    mybalance[0,$2] -= amount}function dotransfer(mybalance) {    mybalance[0,$2] -= amount    mybalance[curmonth,$2] -= amount    mybalance[0,$3] += amount    mybalance[curmonth,$3] += amount}

調用 doincome() 或任何其它函數時,我們將交易記錄到兩個位置 — mybalance[0,category] 和 mybalance[curmonth, category],它們分別表示全年的分類帳余額和當月的分類帳余額。這讓我們稍后可以輕松地生成年度或月度收入/支出明細分類帳。

如果研究這些函數,將發現在我的引用中傳遞了 mybalance 引用的數組。另外,我們還引用了幾個全局變量:curmonth,它保存了當前記錄所屬的月份的數值,$2(費用分類帳),$3(收入分類帳)和金額($7,美元金額)。調用 doincome() 和其它函數時,已經為要處理的當前記錄(行)正確設置了所有這些變量。

主塊

以下是主代碼塊,它包含了分析每一行輸入數據的代碼。請記住,由于正確設置了 FS,可以用 $ 1 引用第一個字段,用 $2 引用第二個字段,依次類推。調用 doincome() 和其它函數時,這些函數可以從函數內部訪問 curmonth、$2、$3 和金額的當前值。請先研究代碼,在代碼之后可以見到我的說明。

balance,第 3 部分

{    curmonth=monthdigit(substr($1,4,3))    amount=$7        #record all the categories encountered    if ( $2 != "-" )        globcat[$2]="yes"    if ( $3 != "-" )        globcat[$3]="yes"    #tally up the transaction properly    if ( $2 == "-" ) {        if ( $3 == "-" ) {            print "Error: inc and exp fields are both blank!"            exit 1        } else {            #this is income            doincome(balance)            if ( $5 == "Y" )                doincome(balance2)        }    } else if ( $3 == "-" ) {        #this is an expense         doexpense(balance)        if ( $5 == "Y" )             doexpense(balance2)    } else {        #this is a transfer        dotransfer(balance)        if ( $5 == "Y" )             dotransfer(balance2)    }           }

在主塊中,前兩行將 curmonth 設置成 1 到 12 之間的整數,并將金額設置成字段 7(使代碼易于理解)。然后,是四行有趣的代碼,它們將值寫到數組 globcat 中。globcat,或稱作全局分類帳數組,用于記錄在文件中遇到的所有分類帳 — “inco”、”misc”、”food”、”util” 等。例如,如果 $2 == “inco”,則將 globcat["inco"] 設置成 “yes”。稍后,我們可以使用簡單的 “for (x in globcat)” 循環來迭代分類帳列表。

在接著的大約二十行中,我們分析字段 $2 和 $3,并適當記錄交易。如果 $2==”-” 且 $3!=”-”,表示我們有收入,因此調用 doincome()。如果是相反的情況,則調用 doexpense();如果 $2 和 $3 都包含分類帳,則調用 dotransfer()。每次我們都將 “balance” 數組傳遞給這些函數,從而在這些函數中記錄適當的數據。

您還會發現幾行代碼說“if ( $5 == “Y” ),那么將同一個交易記錄到 balance2 中”。我們在這里究竟做了些什么?您將回憶起 $5 包含 “Y” 或 “N”,并記錄交易是否已經過帳到帳戶。由于僅當過帳了交易時我們才將交易記錄到 balance2,因此 balance2 包含了真實的帳戶余額,而 “balance” 包含了所有交易,不管是否已經過帳??梢允褂?balance2 來驗證數據項(因為它應該與當前銀行帳戶余額匹配),可以使用 “balance” 來確保沒有透支帳戶(因為它會考慮您開出的尚未兌現的所有支票)。

生成報表

主塊重復處理了每一行記錄之后,現在我們有了關于比較全面的、按分類帳和按月份劃分的借方和貸方記錄?,F在,在這種情況下最合適的做法是只須定義生成報表的 END 塊:

balance,第 4 部分

END {    bal=0    bal2=0      for (x in globcat) {        bal=bal+balance[0,x]        bal2=bal2+balance2[0,x]    }    printf("Your available funds: %10.2f/n", bal)    printf("Your account balance: %10.2f/n", bal2)  }

這個報表將打印出匯總,如下所示:

Your available funds:1174.22Your account balance:2399.33

在 END 塊中,我們使用 “for (x in globcat)” 結構來迭代每一個分類帳,根據記錄在案的交易結算主要余額。實際上,我們結算兩個余額,一個是可用資金,另一個是帳戶余額。要執行程序并處理您在文件 “mycheckbook.txt” 中輸入的財務數據,將以上所有代碼放入文本文件 “balance”,執行 “chmod +x balance”,然后輸入 “./balance mycheckbook.txt”。然后 balance 腳本將合計所有交易,打印出兩行余額匯總。

升級

我使用這個程序的更高級版本來管理我的個人和企業財務。我的版本(由于篇幅限制不能在此涵蓋)會打印出收入和費用的月度明細分類帳,包括年度總合、凈收入和其它許多內容。它甚至以 HTML 格式輸出數據,因此我可以在 Web 瀏覽器中查看它。:) 如果您認為這個程序有用,我建議您將這些特性添加到這個腳本中。不必將它配置成要 記錄 任何附加信息;所需的全部信息已經在 balance 和 balance2 里面了。只要升級 END 塊就萬事具備了!

本系列教程轉自: Developer Works of  IBM


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品日本美女福利在线观看| 欧美亚洲成人xxx| 欧美多人乱p欧美4p久久| 狠狠躁18三区二区一区| 日韩av123| 久久久精品久久久| 青青青国产精品一区二区| 久久久久久网站| 亚洲激情自拍图| 日韩电影网在线| 欧美性jizz18性欧美| 久久久精品影院| 国产在线98福利播放视频| 91免费国产视频| 欧美大片网站在线观看| 日韩av黄色在线观看| 欧美黑人性视频| 日韩福利视频在线观看| 国产精品久久久久久久7电影| 精品国产乱码久久久久久天美| 在线观看国产精品淫| 亚洲国产精品视频在线观看| 国产精品扒开腿爽爽爽视频| 欧美日韩精品国产| 日韩精品亚洲元码| 欧美日韩国产一区二区| 91精品国产九九九久久久亚洲| 久热精品视频在线免费观看| 亚洲综合视频1区| 久久精品国产一区二区电影| 国产精品igao视频| 国产精品美乳在线观看| 中文字幕在线精品| 日韩小视频在线观看| 久精品免费视频| 国产视频999| 国产精品一区=区| 国产精品一区二区电影| 精品性高朝久久久久久久| 久久久精品999| 亚洲欧美日韩中文视频| 精品亚洲夜色av98在线观看| 亚洲人成网7777777国产| 韩国视频理论视频久久| 亚洲国产精品中文| 国产亚洲人成网站在线观看| 亚洲在线视频观看| 久久夜色精品亚洲噜噜国产mv| 91禁外国网站| 97在线视频免费观看| 国产精品白嫩美女在线观看| 亚洲国产成人精品一区二区| 91中文字幕在线| 国产一区二区三区在线播放免费观看| 欧美激情一区二区三区在线视频观看| 欧美亚洲成人xxx| 疯狂欧美牲乱大交777| 在线观看国产精品淫| 国产精品一区二区三区成人| 国产一区二区三区精品久久久| 亚洲精品美女在线观看| 国产精品午夜视频| 韩国欧美亚洲国产| 欧美视频在线观看免费网址| 日韩欧美在线第一页| 国产精品久久久久久av福利| 亚洲欧美视频在线| 欧美大尺度电影在线观看| 午夜精品在线视频| 一区二区日韩精品| 久久久久久伊人| 亚洲人成网站777色婷婷| 精品女厕一区二区三区| 欧美性生活大片免费观看网址| 奇门遁甲1982国语版免费观看高清| 久久久免费在线观看| 日韩禁在线播放| 国产成人精品亚洲精品| 欧美寡妇偷汉性猛交| 日本久久亚洲电影| 91精品国产乱码久久久久久久久| 欧美成aaa人片在线观看蜜臀| 亚洲人成电影在线观看天堂色| 懂色av一区二区三区| 国产亚洲精品va在线观看| 日韩欧美成人精品| 91久久国产婷婷一区二区| 国内精品中文字幕| 亚洲欧洲美洲在线综合| 国产精品一区二区久久久| 亚洲影视中文字幕| 高清一区二区三区日本久| 成人信息集中地欧美| 久久的精品视频| 久久免费高清视频| 日韩av手机在线| 国产97在线|日韩| 欧美日韩亚洲国产一区| 91精品国产91久久久久久不卡| 国产精品美乳一区二区免费| 亚洲aⅴ男人的天堂在线观看| 亚洲视频国产视频| 欧美激情视频一区二区| 国产不卡精品视男人的天堂| 亚洲美女av在线播放| 97香蕉超级碰碰久久免费的优势| 国产欧美韩国高清| 欧美在线视频一区二区| 成人免费福利视频| 欧美体内谢she精2性欧美| 国产精品午夜一区二区欲梦| 日韩欧美成人网| 国产精品一区=区| 国产日产欧美精品| 国产主播欧美精品| 亚洲人午夜精品| 欧美成人午夜影院| 日韩av手机在线观看| 亚洲福利精品在线| 国产在线视频2019最新视频| xvideos亚洲| 久久中国妇女中文字幕| 91久久久久久国产精品| 成人在线中文字幕| 日韩电影大全免费观看2023年上| 日韩国产精品一区| 久久精品中文字幕一区| 菠萝蜜影院一区二区免费| 亚洲欧美日本另类| 69视频在线播放| 欧美视频裸体精品| 亚洲bt欧美bt日本bt| 日韩av片永久免费网站| 色综合天天综合网国产成人网| 国产日韩欧美日韩| 日韩在线视频播放| 亚洲网站在线播放| 欧美在线视频a| 日韩美女福利视频| 日韩av网站大全| 亚洲欧美日韩中文视频| 午夜精品福利在线观看| 国产成人aa精品一区在线播放| 亚洲免费人成在线视频观看| 亚洲精品999| 国产91精品久久久久久| 成人精品一区二区三区| 欧美激情视频网站| 欧美乱人伦中文字幕在线| 夜夜嗨av色综合久久久综合网| 91精品啪aⅴ在线观看国产| 久久在线免费视频| 亚洲国产黄色片| 久久久久国产精品一区| 成人黄色av网站| 亚洲va欧美va国产综合剧情| 欧美日韩亚洲视频一区| 久久精品国产免费观看| 亚洲精品一区中文字幕乱码| 精品国产91久久久| 久久天天躁夜夜躁狠狠躁2022| 中文字幕亚洲国产| 亚洲黄色在线观看| 国产成人综合精品在线|