天天看點

awk

Awk簡介  AWK是一種優良的文本處理工具。它不僅是 Linux 中也是任何環境中現有的功能最強大的資料處理引擎之一。這種程式設計及資料操作語言(其名稱得自于它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母)的最大功能取決于一個人所擁有的知識。 AWK 提供了極其強大的功能:可以進行樣式裝入、流控制、數學運算符、程序控制語句甚至于内置的變量和函數。它具備了一個完整的語言所應具有的幾乎所有精美特性。實際上 AWK 的确擁有自己的語言: AWK 程式設計語言, 三位建立者已将它正式定義為“樣式掃描和處理語言”。它允許您建立簡短的程式,這些程式讀取輸入檔案、為資料排序、處理資料、對輸入執行計算以及生成報表,還有無數其他的功能。

  最簡單地說, AWK 是一種用于處理文本的程式設計語言工具。AWK 在很多方面類似于 shell 程式設計語言,盡管 AWK 具有完全屬于其本身的文法。它的設計思想來源于 SNOBOL4 、sed 、Marc Rochkind設計的有效性語言、語言工具 yacc 和 lex ,當然還從 C 語言中擷取了一些優秀的思想。在最初創造 AWK 時,其目的是用于文本處理,并且這種語言的基礎是,隻要在輸入資料中有模式比對,就執行一系列指令。該實用工具掃描檔案中的每一行,查找與指令行中所給定内容相比對的模式。如果發現比對内容,則進行下一個程式設計步驟。如果找不到比對内容,則繼續處理下一行。

  盡管操作可能會很複雜,但指令的文法始終是:

  awk '{pattern + action}'

  其中 pattern 表示 AWK 在資料中查找的内容,而 action 是在找到比對内容時所執行的一系列指令。花括号 ({}) 不需要在程式中始終出現,但它們用于根據特定的模式對一系列指令進行分組。

  gawk 是 AWK 的 GNU 版本。

  一般的UNIX作業系統,本身即附有AWK,不同的UNIX作業系統所附的AWK其版本亦不盡相同,若讀者所使用的系統上未附有AWK,可通過 anonymous ftp 到下列地方取得:

  phi.sinica.edu.tw:/pub/gnu

  ftp.edu.tw:/UNIX/gnu

  prep.ai.mit.edu:/pub/gnu

  注 解:一種程式設計語言,因其模式比對文法而特别有用,通常用于資料檢索和資料轉換。一個GNU版本稱為Gawk。

  awk提供了适應多種需要的不同解決方案,它們是:

  一、 awk指令行,你可以象使用普通UNIX指令一樣使用awk,在指令行中你也可以使用awk程式設計語言,雖然awk支援多行的錄入,但是錄入長長的指令行并保證其正确無誤卻是一件令人頭疼的事,是以,這種方法一般隻用于解決簡單的問題。當然,你也可以在shell script程式中引用awk指令行甚至awk程式腳本。

  二、使用-f選項調用awk程式。awk允許将一段awk程式寫入一個文本檔案,然後在awk指令行中用-f選項調用并執行這段程式。具體的方法我們将在後面的awk文法中講到。

  三、利用指令解釋器調用awk程式:利用UNIX支援的指令解釋器功能,我們可以将一段awk程式寫入文本檔案,然後在它的第一行加上:

  #!/bin/awk -f

  并賦予這個文本檔案以執行的權限。這樣做之後,你就可以在指令行中用類似于下面這樣的方式調用并執行這段awk程式了。

  awk腳本文本名 待處理檔案

  與其它UNIX指令一樣,awk擁有自己的文法:

  awk [ -F re] [parameter...] ['prog'] [-f progfile]

  參數說明:

  -F re:允許awk更改其字段分隔符。

  parameter: 該參數幫助為不同的變量指派。

  'prog': awk的程式語句段。這個語句段必須用單拓号:'和'括起,以防被shell解釋。這個程式語句段的标準形式為:

  'pattern '

  其中pattern參數可以是egrep正規表達式中的任何一個,它可以使用文法/re/再加上一些樣式比對技巧構成。與sed類似,你也可以使用"," 分開兩樣式以選擇某個範圍。關于比對的細節,你可以參考附錄,如果仍不懂的話,找本UNIX書學學grep和sed(本人是在學習ed時掌握比對技術的)。 action參數總是被大括号包圍,它由一系列awk語句組成,各語句之間用";"分隔。awk解釋它們,并在pattern給定的樣式比對的記錄上執行其操作。與shell類似,你也可以使用“#”作為注釋符,它使“#”到行尾的内容成為注釋,在解釋執行時,它們将被忽略。你可以省略pattern和 action之一,但不能兩者同時省略,當省略pattern時沒有樣式比對,表示對所有行(記錄)均執行操作,省略action時執行預設的操作——在标準輸出上顯示。

  -f progfile:允許awk調用并執行progfile指定有程式檔案。progfile是一個文本檔案,他必須符合awk的文法。

  in_file:awk的輸入檔案,awk允許對多個輸入檔案進行處理。值得注意的是awk不修改輸入檔案。如果未指定輸入檔案,awk将接受标準輸入,并将結果顯示在标準輸出上。awk支援輸入輸出重定向。

  前面說過,awk處理的工作與資料庫的處理方式有相同之處,其相同處之一就是awk支援對記錄和字段的處理,其中對字段的處理是grep和sed不能實作的,這也是awk優于二者的原因之一。在awk中,預設的情況下總是将文本檔案中的一行視為一個記錄,而将一行中的某一部分作為記錄中的一個字段。為了操作這些不同的字段,awk借用shell的方法,用1,2,3...這樣的方式來順序地表示行(記錄)中的不同字段。特殊地,awk用0表示整個行(記錄)。不同的字段之間是用稱作分隔符的字元分隔開的。系統預設的分隔符是空格。awk允許在指令行中用-F re的形式來改變這個分隔符。事實上,awk用一個内置的變量FS來記憶這個分隔符。awk中有好幾個這樣的内置變量,例如,記錄分隔符變量RS、目前工作的記錄數NR等等,本文後面的附表列出了全部的内置變量。這些内置的變量可以在awk程式中引用或修改,例如,你可以利用NR變量在模式比對中指定工作範圍,也可以通過修改記錄分隔符RS讓一個特殊字元而不是換行符作為記錄的分隔符。

  例:顯示文本檔案myfile中第七行到第十五行中以字元%分隔的第一字段,第三字段和第七字段:

  awk -F % 'NR==7,NR==15 {print $1 $3 $7}' myfile

  awk 之是以成為一種優秀的程式設計語言的原因之一是它吸收了某些優秀的程式設計語言(例如C)語言的許多優點。這些優點之一就是内置函數的使用,awk定義并支援了一系列的内置函數,由于這些函數的使用,使得awk提供的功能更為完善和強大,例如,awk使用了一系列的字元串處理内置函數(這些函數看起來與C 語言的字元串處理函數相似,其使用方式與C語言中的函數也相差無幾),正是由于這些内置函數的使用,使awk處理字元串的功能更加強大。本文後面的附錄中列有一般的awk所提供的内置函數,這些内置函數也許與你的awk版本有些出入,是以,在使用之前,最好參考一下你的系統中的聯機幫助。

  内置的字元串函數

gsub(r,s)

在整個$0中用s代替r

gsub(r,s,t)

在整個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)

傳回經fmt格式化後的exp

sub(r,s)

用$0中最左邊最長的子串代替s

substr(s,p)

傳回字元串s中從p開始的字尾部分

substr(s,p,n)

傳回字元串s中從p開始長度為n的字尾部分

 gsub函數有點類似于sed查找和替換。它允許替換一個字元串或字元為另一個字元串或字元,并以正規表達式的形式執行。第一個函數作用于記錄$0,第二個gsub函數允許指定目标,然而,如果未指定目标,預設為$0。

  index(s,t)函數傳回目标字元串s中查詢字元串t的首位置。length函數傳回字元串s字元長度。

  match函數測試字元串s是否包含一個正規表達式r定義的比對。

  split使用域分隔符fs将字元串s劃分為指定序列a。

  sprint函數類似于printf函數,傳回基本輸出格式fmt的結果字元串exp。

  sub(r,s)函數将用s替代$0中最左邊最長的子串,該子串被r比對。

  sub(s,p)傳回字元串s在位置p後的字尾。

  substr(s,p,n)同上,并指定子串長度為n。

  按照順序,我們應當講解awk程式設計的内容了,但在講解之前,我們将用一些例子來對前面的知識進行回顧,這些例子都是在指令行中使用的,由此我們可以知道在指令行中使用awk是多麼的友善。這樣做的原因一方面是為下面的内容作鋪墊,另一方面是介紹一些解決簡單問題的方法,我們完全沒有必要用複雜的方法來解決簡單的問題----既然awk提供了較為簡單的方法的話。

  例:顯示文本檔案mydoc比對(含有)字元串"sun"的所有行。

  awk '/sun/' mydoc

  由于顯示整個記錄(全行)是awk的預設動作,是以可以省略action項。

  例:下面是一個較為複雜的比對的示例:

  awk '/[Ss]un/,/[Mm]oon/ ' myfile

  它将顯示第一個比對Sun或sun的行與第一個比對Moon或moon的行之間的行,并顯示到标準輸出上。

  例:下面的示例顯示了内置變量和内置函數length()的使用:

  awk 'length($0)>80 {print NR}' myfile

  該指令行将顯示文本myfile中所有超過80個字元的行号,在這裡,用0表示整個記錄(行),同時,内置變量NR不使用标志符''。

  例:作為一個較為實際的例子,我們假設要對UNIX中的使用者進行安全性檢查,方法是考察/etc下的passwd檔案,檢查其中的passwd字段(第二字段)是否為"*",如不為"*",則表示該使用者沒有設定密碼,顯示出這些使用者名(第一字段)。我們可以用如下語句實作:

  #awk -F: '$2!="*" {printf("%s no password!\n",$1)'} /etc/passwd

  在這個示例中,passwd檔案的字段分隔符是“:”,是以,必須用-F:來更改預設的字段分隔符,這個示例中也涉及到了内置函數printf的使用。

  awk内置變量詳細介紹

  awk提供了有很多内置變量,如果你經常使用awk處理文本,了解這些這些内置變量的使用是很有必要的。下面看一下這些内置變量的詳細介紹。

  ARGC 指令行參數個數(不包括awk的選項和awk的程式内容)

  ARGIND 目前正在處理的ARGV中的檔案的索引值(同時處理多個檔案時會用到)

  ARGV 指令行參數序列數組,下标從0開始

  CONVFMT 數字轉換格式,和C語言中的數字輸出格式化類似, 預設為"%.6g"

  ENVIRON 目前系統的環境變量

  ERRNO 出錯時的錯誤資訊

  FIELDWIDTHS 以空格分隔的字段寬度,如果指定此變量,awk将會用指定的寬度替換變量FS指定的分隔符。看下面的例子:

  [LinuxSong@test /tmp]$ cat t

  abcdefasfa

  [LinuxSong@test /tmp]$ awk 'BEGIN {FIELDWIDTHS="2 3 4"}{print $1,$2,$3}' t

  ab cde fasf

  FILENAME 目前正在處理的檔案名,該變量不能在BEGIN塊中使用。

  FNR 目前處理的記錄号

  FS 字段的分隔符,預設為空格。

  IGNORECASE 如果該變量設定為非0值,在進行字元串比對時忽略大小寫。

  NF 目前記錄中的字段個數

  NR 已經讀出的記錄數

  OFMT 數字的輸出格式

  OFS 輸出的字段分隔符,預設為空格

  ORS 輸出的記錄分隔符,預設為新行

  RS 輸入記錄的分隔符,預設為新行

  RSTART 被match()函數比對的字元串的起始位置,如果沒有比對則為0(從1開始)

  RLENGTH 被match()函數比對的字元串的長度

  SUBSEP 數組中多個下标的分隔符,預設為"\034"

  如同其它程式設計語言一樣,awk允許在程式語言中設定變量,事實上,提供變量的功能是程式設計語言的基本要求,不提供變量的程式設計語言本人還從未見過。

  awk 提供兩種變量,一種是awk内置的變量,這前面我們已經講過,需要着重指出的是,與後面提到的其它變量不同的是,在awk程式中引用内置變量不需要使用标志符""(回憶一下前面講過的NR的使用)。awk提供的另一種變量是自定義變量。awk允許使用者在awk程式語句中定義并調用自已的變量。當然這種變量不能與内置變量及其它awk保留字相同,在awk中引用自定義變量必須在它前面加上标志符""。與C語言不同的是,awk中不需要對變量進行初始化, awk根據其在awk中第一次出現的形式和上下文确定其具體的資料類型。當變量類型不确定時,awk預設其為字元串類型。這裡有一個技巧:如果你要讓你的 awk程式知道你所使用的變量的明确類型,你應當在在程式中給它賦初值。在後面的執行個體中,我們将用到這一技巧。

  運算與判斷:

  作為一種程式設計語言所應具有的特點之一,awk支援多種運算,這些運算與C語言提供的幾本相同:如+、-、*、/、%等等,同時,awk也支援C語言中類似++、--、+=、-=、=+、=-之類的功能,這給熟悉C語言的使用者編寫awk程式帶來了極大的友善。作為對運算功能的一種擴充,awk還提供了一系列内置的運算函數(如log、sqr、cos、sin等等)和一些用于對字元串進行操作(運算)的函數(如length、substr等等)。這些函數的引用大大的提高了awk的運算功能。

  作為對條件轉移指令的一部分,關系判斷是每種程式設計語言都具備的功能,awk也不例外。awk 中允許進行多種測試,如常用的==(等于)、!=(不等于)、>(大于)、=(大于等于)、>=(小于等于)等等,同時,作為樣式比對,還提供了~(比對于)和!~(不比對于)判斷。

  作為對測試的一種擴充,awk也支援用邏輯運算符:!(非)、&&(與)、||(或)和括号()進行多重判斷,這大大增強了awk的功能。本文的附錄中列出了awk所允許的運算、判斷以及操作符的優先級。

  流程控制語句是任何程式設計語言都不能缺少的部分。任何好的語言都有一些執行流程控制的語句。awk提供的完備的流程控制語句類似于C語言,這給我們程式設計帶來了極大的友善。

  1、BEGIN和END:

  在awk 中兩個特别的表達式,BEGIN和END,這兩者都可用于pattern中(參考前面的awk文法),提供BEGIN和END的作用是給程式賦予初始狀态和在程式結束之後執行一些掃尾的工作。任何在BEGIN之後列出的操作(在{}内)将在awk開始掃描輸入之前執行,而END之後列出的操作将在掃描完全部的輸入之後執行。是以,通常使用BEGIN來顯示變量和預置(初始化)變量,使用END來輸出最終結果。

  例:累計銷售檔案xs中的銷售金額(假設銷售金額在記錄的第三字段):

  awk

  >'BEGIN { FS=":";print "統計銷售金額";total=0}

  >{print 3;total=total+3;}

  >END {printf "銷售金額總計:%.2f",total}' sx

  (注:>是shell提供的第二提示符,如要在shell程式awk語句和awk語言中換行,則需在行尾加反斜杠\)

  在這裡,BEGIN預置了内部變量FS(字段分隔符)和自定義變量total,同時在掃描之前顯示出輸出行頭。而END則在掃描完成後列印出總合計。

  2、流程控制語句

  awk提供了完備的流程控制語句,其用法與C語言類似。下面我們一一加以說明:

  2.1、if...else語句:

  格式:

  if(表達式)

  語句1

  else

  語句2

  格式中"語句1"可以是多個語句,如果你為了友善awk判斷也友善你自已閱讀,你最好将多個語句用{}括起來。awk分枝結構允許嵌套,其格式為:

  if(表達式1)

  {if(表達式2)

  }

  語句3

  else {if(表達式3)

  語句4

  語句5

  語句6

  當然實際操作過程中你可能不會用到如此複雜的分枝結構,這裡隻是為了給出其樣式罷了。

  2.2、while語句

  格式為:

  while(表達式)

  語句

  2.3、do-while語句

  do

  {

  }while(條件判斷語句)

  2.4、for語句

  格式為:

  for(初始表達式;終止條件;步長表達式)

  {語句}

  在awk 的 while、do-while和for語句中允許使用break,continue語句來控制流程走向,也允許使用exit這樣的語句來退出。break 中斷目前正在執行的循環并跳到循環外執行下一條語句。continue從目前位置跳到循環開始處執行。對于exit的執行有兩種情況:當exit語句不在 END中時,任何操作中的exit指令表現得如同到了檔案尾,所有模式或操作執行将停止,END模式中的操作被執行。而出現在END中的exit将導緻程式終止。

  例:為了

  定義和調用使用者自己的函數是幾乎每個進階語言都具有的功能,awk也不例外,但原始的awk并不提供函數功能,隻有在nawk或較新的awk版本中才可以增加函數。

  函數的使用包含兩部分:函數的定義與函數調用。其中函數定義又包括要執行的代碼(函數本身)和從主程式代碼傳遞到該函數的臨時調用。

  awk函數的定義方法如下:

  function 函數名(參數表){

  函數體

  在gawk中允許将function省略為func,但其它版本的awk不允許。函數名必須是一個合法的标志符,參數表中可以不提供參數(但在調用函數時函數名後的一對括号仍然是不可缺少的),也可以提供一個或多個參數。與C語言相似,awk的參數也是通過值來傳遞的。

  在awk 中調用函數比較簡單,其方法與C語言相似,但awk比C語言更為靈活,它不執行參數有效性檢查。換句話說,在你調用函數時,可以列出比函數預計(函數定義中規定)的多或少的參數,多餘的參數會被awk所忽略,而不足的參數,awk将它們置為預設值0或空字元串,具體置為何值,将取決于參數的使用方式。

  awk函數有兩種傳回方式:隐式傳回和顯式傳回。當awk執行到函數的結尾時,它自動地傳回到調用程式,這是函數是隐式傳回的。如果需要在結束之前退出函數,可以明确地使用傳回語句提前退出。方法是在函數中使用形如:return 傳回值 格式的語句。

  例:下面的例子示範了函數的使用。在這個示例中,定義了一個名為print_header的函數,該函數調用了兩個參數FileName和 PageNum, FileName參數傳給函數目前使用的檔案名,PageNum參數是目前頁的頁号。這個函數的功能是列印(顯示)出目前檔案的檔案名,和目前頁的頁号。完成這個功能後,這個函數将傳回下一頁的頁号。

  nawk

  >'BEGIN{pageno=1;file=FILENAME

  >pageno=print_header(file,pageno);#調用函數print_header

  >printf("目前頁頁号是:%d\n",pageno);

  >}

  >#定義函數print_header

  >function print_header(FileName,PageNum){

  >printf("%s %d\n",FileName,PageNum); >PageNum++;return PageNUm;

  >}' myfile

  執行這個程式将顯示如下内容:

  myfile 1

  目前頁頁号是:2

  1.讀取下一條記錄:

  awk的next語句導緻awk讀取下一個記錄并完成模式比對,然後立即執行相應的操作。通常它用比對的模式執行操作中的代碼。next導緻這個記錄的任何額外比對模式被忽略。

  2.簡單地讀取一條記錄

  awk 的 getline語句用于簡單地讀取一條記錄。如果使用者有一個資料記錄類似兩個實體記錄,那麼getline将尤其有用。它完成一般字段的分離(設定字段變量0 FNR NF NR)。如果成功則傳回1,失敗則傳回0(到達檔案尾)。如果需簡單地讀取一個檔案,則可以編寫以下代碼:

  例:示例getline的使用

  {while(getline==1)

  #process the inputted fields

  也可以使getline儲存輸入資料在一個字段中,而不是通過使用getline variable的形式處理一般字段。當使用這種方式時,NF被置成0,FNR和NR被增值。

  使用者也可以使用getline"datafile"

  或

  printf("hello word!\n"

  >>"datafile"

  3. 輸出記錄

  可以通過print 和 printf 将結果輸出:

  awk '{ print $1, $2 }' file

  4.輸出到一個指令

  awk中允許用如下方式将結果輸出到一個指令:

  |"sort-t','"

  awk與shell script混合程式設計

  因為awk可以作為一個shell指令使用,是以awk能與shell批處理程式很好的融合在一起,這給實作awk與shell程式的混合程式設計提供了可能。實作混合程式設計的關鍵是awk與shell script之間的對話,換言之,就是awk與shell script之間的資訊交流:awk從shell script中擷取所需的資訊(通常是變量的值)、在awk中執行shell指令行、shell script将指令執行的結果送給awk處理以及shell scri

繼續閱讀