awk 详解
AWK
簡介
AWK是一種優良的文本處理工具。它不僅是 Linux 中也是任何環境中現有的功能最強大的數據處理引擎之一。這種編程及數據操作語言(其名稱得自于它的創始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首個字母)的最大功能取決于一個人所擁有的知識。AWK 提供了極其強大的功能:可以進行樣式裝入、流控制、數學運算符、進程控制語句甚至于內置的變量和函數。它具備了一個完整的語言所應具有的幾乎所有精美特性。實際上 AWK 的確擁有自己的語言:AWK 程序設計語言, 三位創建者已將它正式定義為“樣式掃描和處理語言”。它允許您創建簡短的程序,這些程序讀取輸入文件、為數據排序、處理數據、對輸入執行計算以及生成報表,還有無數其他的功能。
使用方法
awk '{pattern + action}' {filenames}
盡管操作可能會很復雜,但語法總是這樣,其中 pattern 表示 AWK 在數據中查找的內容,而 action 是在找到匹配內容時所執行的一系列命令。花括號({})不需要在程序中始終出現,但它們用于根據特定的模式對一系列指令進行分組。 pattern就是要表示的正則表達式,用斜杠括起來。awk語言的最基本功能是在文件或者字符串中基于指定規則瀏覽和抽取信息,awk抽取信息后,才能進行其他文本操作。完整的awk腳本通常用來格式化文本文件中的信息。通常,awk是以文件的一行為處理單位的。awk每接收文件的一行,然后執行相應的命令,來處理文本。
調用awk
- 命令行方式
awk [-F field-separator] 'commands' input-file(s)
其中,commands 是真正awk命令,[-F域分隔符]是可選的。 input-file(s) 是待處理的文件。
在awk中,文件的每一行中,由域分隔符分開的每一項稱為一個域。通常,在不指名-F域分隔符的情況下,默認的域分隔符是空格。
- shell腳本方式
將所有的awk命令插入一個文件,并使awk程序可執行,然后awk命令解釋器作為腳本的首行,一遍通過鍵入腳本名稱來調用。
相當于shell腳本首行的:#!/bin/sh
可以換成:#!/bin/awk
- 將所有的awk命令插入一個單獨文件,然后調用:
awk -f awk-script-file input-file(s)
其中,-f選項加載awk-script-file中的awk腳本,input-file(s)跟上面的是一樣的。
入門實例(取出linux最近登錄的五個用戶的用戶名)
# 下面是取出最近登錄的五個用戶的信息[root@localhost ~]# last -n 5 root pts/1 192.168.1.100 Tue Feb 10 11:21 still logged in test pts/1 192.168.1.100 Tue Feb 10 00:46 - 02:28 (01:41) root pts/1 192.168.1.100 Mon Feb 9 11:41 - 18:30 (06:48) dmtsai pts/1 192.168.1.100 Mon Feb 9 11:41 - 11:41 (00:00) root tty1 120.239.75.53 Fri Sep 5 14:09 - 14:10 (00:01)# 如果只是顯示最近登錄的5個帳號的用戶名$ last -n 5 | awk '{print $1}' root test root dmtsai root# awk工作流程是這樣的:讀入有'\n'換行符分割的一條記錄,然后將記錄按指定的域分隔符劃分域,填充域,$0則表示所有域,$1表示第一個域,$n表示第n個域。默認域分隔符是"空白鍵" 或 "[tab]鍵",所以$1表示登錄用戶,$3表示登錄用戶ip,以此類推。入門實例(只顯示/etc/passwd的賬戶)
$ cat /etc/passwd | awk -F ':' '{print $1}' root daemon bin sys# 這種是awk+action的示例,每行都會執行action{print $1}。# -F指定域分隔符為':'。# 如果只是顯示/etc/passwd的賬戶和賬戶對應的shell,而賬戶與shell之間以tab鍵分割$ cat /etc/passwd | awk -F ':' '{print $1"\t"$7}' root /bin/bash daemon /bin/sh bin /bin/sh sys /bin/sh# 如果只是顯示/etc/passwd的賬戶和賬戶對應的shell,而賬戶與shell之間以逗號分割,而且在所有行添加列名name,shell,在最后一行添加"blue,/bin/nosh"。$ cat /etc/passwd |awk -F ':' 'BEGIN {print "name,shell"} {print $1","$7} END {print "blue,/bin/nosh"}' name,shell root,/bin/bash daemon,/bin/sh bin,/bin/sh sys,/bin/sh .... blue,/bin/nosh# awk工作流程是這樣的:先執行BEGING,然后讀取文件,讀入有/n換行符分割的一條記錄,然后將記錄按指定的域分隔符劃分域,填充域,$0則表示所有域,$1表示第一個域,$n表示第n個域,隨后開始執行模式所對應的動作action。接著開始讀入第二條記錄······直到所有的記錄都讀完,最后執行END操作。# 搜索/etc/passwd有root關鍵字的所有行$ awk -F: '/root/' /etc/passwd root:x:0:0:root:/root:/bin/bash# 這種是pattern的使用示例,匹配了pattern(這里是root)的行才會執行action(沒有指定action,默認輸出每行的內容)。# 搜索支持正則,例如找root開頭的: awk -F: '/^root/' /etc/passwd# 搜索/etc/passwd有root關鍵字的所有行,并顯示對應的shell$ awk -F: '/root/{print $7}' /etc/passwd /bin/bash# 這里指定了action{print $7}awk 內置變量
awk有許多內置變量用來設置環境信息,這些變量可以被改變,下面給出了最常用的一些變量。
$n # 當前記錄的第n個字段,比如n為1表示第一個字段,n為2表示第二個字段。$0 # 這個變量包含執行過程中當前行的文本內容。ARGC # 命令行參數的數目。ARGIND # 命令行中當前文件的位置(從0開始算)。ARGV # 包含命令行參數的數組。CONVFMT # 數字轉換格式(默認值為%.6g)。ENVIRON # 環境變量關聯數組。ERRNO # 最后一個系統錯誤的描述。FIELDWIDTHS # 字段寬度列表(用空格鍵分隔)。FILENAME # 當前輸入文件的名。NR # 表示記錄數,在執行過程中對應于當前的行號FNR # 同NR,但相對于當前文件。FS # 字段分隔符(默認是任何空格)。IGNORECASE # 如果為真,則進行忽略大小寫的匹配。NF # 表示字段數,在執行過程中對應于當前的字段數。 print $NF答應一行中最后一個字段OFMT # 數字的輸出格式(默認值是%.6g)。OFS # 輸出字段分隔符(默認值是一個空格)。ORS # 輸出記錄分隔符(默認值是一個換行符)。RS # 記錄分隔符(默認是一個換行符)。RSTART # 由match函數所匹配的字符串的第一個位置。RLENGTH # 由match函數所匹配的字符串的長度。SUBSEP # 數組下標分隔符(默認值是34)。此外,$0變量是指整條記錄。$1表示當前行的第一個域,$2表示當前行的第二個域,......以此類推。
統計/etc/passwd:文件名,每行的行號,每行的列數,對應的完整行內容:
$ awk -F ':' '{print "filename:" FILENAME ",linenumber:" NR ",columns:" NF ",linecontent:"$0}' /etc/passwd filename:/etc/passwd,linenumber:1,columns:7,linecontent:root:x:0:0:root:/root:/bin/bash filename:/etc/passwd,linenumber:2,columns:7,linecontent:daemon:x:1:1:daemon:/usr/sbin:/bin/sh filename:/etc/passwd,linenumber:3,columns:7,linecontent:bin:x:2:2:bin:/bin:/bin/sh filename:/etc/passwd,linenumber:4,columns:7,linecontent:sys:x:3:3:sys:/dev:/bin/sh使用printf替代print,可以讓代碼更加簡潔,易讀
awk -F ':' '{printf("filename:%s,linenumber:%s,columns:%s,linecontent:%s\n",FILENAME,NR,NF,$0)}' /etc/passwd
print 和 printf
awk中同時提供了print和printf兩種打印輸出的函數。其中print函數的參數可以是變量、數值或者字符串。字符串必須用雙引號引用,參數用逗號分隔。如果沒有逗號,參數就串聯在一起而無法區分。這里,逗號的作用與輸出文件的分隔符的作用是一樣的,只是后者是空格而已。printf函數,其用法和c語言中printf基本相似,可以格式化字符串,輸出復雜時,printf更加好用,代碼更易懂。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
awk編程
變量和賦值
除了awk的內置變量,awk還可以自定義變量。
下面統計/etc/passwd的賬戶人數
awk '{count++;print $0;} END{print "user count is ", count}' /etc/passwd root:x:0:0:root:/root:/bin/bash ...... user count is 40count是自定義變量。之前的action{}里都是只有一個print,其實print只是一個語句,而action{}可以有多個語句,以;號隔開。
這里沒有初始化count,雖然默認是0,但是妥當的做法還是初始化為0:
awk 'BEGIN {count=0;print "[start]user count is ", count} {count=count+1;print $0;} END{print "[end]user count is ", count}' /etc/passwd [start]user count is 0 root:x:0:0:root:/root:/bin/bash ... [end]user count is 40統計某個文件夾下的文件占用的字節數
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size}' [end]size is 8657198如果以M為單位顯示:
ls -l |awk 'BEGIN {size=0;} {size=size+$5;} END{print "[end]size is ", size/1024/1024,"M"}' [end]size is 8.25889 M注意,統計不包括文件夾的子目錄。
條件語句
awk中的條件語句是從C語言中借鑒來的,見如下聲明方式:
if (expression) {statement;statement;... ... }if (expression) {statement; } else {statement2; }if (expression) {statement1; } else if (expression1) {statement2; } else {statement3; }統計某個文件夾下的文件占用的字節數,過濾4096大小的文件(一般都是文件夾):
ls -l |awk 'BEGIN {size=0;print "[start]size is ", size} {if($5!=4096){size=size+$5;}} END{print "[end]size is ", size/1024/1024,"M"}' [end]size is 8.22339 M循環語句
awk中的循環語句同樣借鑒于C語言,支持while、do/while、for、break、continue,這些關鍵字的語義和C語言中的語義完全相同。
# for循環for(變量 in 數組) {語句} for(變量;條件;表達式) {語句} # while循環while(表達式) {語句} # do...while循環do {語句} while(條件) # 其他相關語句break:退出程序循環continue: 進入下一次循環next:讀取下一個輸入行exit:退出主輸入循環,進入END,若沒有END或END中有exit語句,則退出腳本。數組
因為awk中數組的下標可以是數字和字母,數組的下標通常被稱為關鍵字(key)。值和關鍵字都存儲在內部的一張針對key/value應用hash的表格里。由于hash不是順序存儲,因此在顯示數組內容時會發現,它們并不是按照你預料的順序顯示出來的。數組和變量一樣,都是在使用時自動創建的,awk也同樣會自動判斷其存儲的是數字還是字符串。一般而言,awk中的數組用來從記錄中收集信息,可以用于計算總和、統計單詞以及跟蹤模板被匹配的次數等等。
顯示/etc/passwd的賬戶
$ awk -F ':' 'BEGIN {count=0;} {name[count] = $1;count++;}; END{for (i = 0; i < NR; i++) print i, name[i]}' /etc/passwd 0 root 1 daemon 2 bin 3 sys 4 sync 5 games ......awk運算
算術運算:(+,-,*,/,&,!,……,++,--)
所有用作算術運算符進行操作時,操作數自動轉為數值,所有非數值都變為0
賦值運算:(=, +=, -=,*=,/=,%=,……=,**=)
邏輯運算符: (||, &&)
關系運算符:(<, <=, >,>=,!=, ==)
正則運算符:(~,~!)(匹配正則表達式,與不匹配正則表達式)
$ awk 'BEGIN{a="100testa";if(a ~ /^100*/){print "ok";}}' ok時間函數
| mktime( YYYY MM dd HH MM ss[ DST]) | 生成時間格式 |
| strftime([format [, timestamp]]) | 格式化時間輸出,將時間戳轉為時間字符串 具體格式,見下表. |
| systime() | 得到時間戳,返回從1970年1月1日開始到當前時間(不計閏年)的整秒數 |
strftime日期和時間格式說明符 :
| %a | 星期幾的縮寫(Sun) |
| %A | 星期幾的完整寫法(Sunday) |
| %b | 月名的縮寫(Oct) |
| %B | 月名的完整寫法(October) |
| %c | 本地日期和時間 |
| %d | 十進制日期 |
| %D | 日期 08/20/99 |
| %e | 日期,如果只有一位會補上一個空格 |
| %H | 用十進制表示24小時格式的小時 |
| %I | 用十進制表示12小時格式的小時 |
| %j | 從1月1日起一年中的第幾天 |
| %m | 十進制表示的月份 |
| %M | 十進制表示的分鐘 |
| %p | 12小時表示法(AM/PM) |
| %S | 十進制表示的秒 |
| %U | 十進制表示的一年中的第幾個星期(星期天作為一個星期的開始) |
| %w | 十進制表示的星期幾(星期天是0) |
| %W | 十進制表示的一年中的第幾個星期(星期一作為一個星期的開始) |
| %x | 重新設置本地日期(08/20/99) |
| %X | 重新設置本地時間(12:00:00) |
| %y | 兩位數字表示的年(99) |
| %Y | 當前月份 |
| %Z | 時區(PDT) |
| %% | 百分號(%) |
轉載于:https://www.cnblogs.com/janlle/p/10779510.html
總結
- 上一篇: 自用懒加载(其实效果并不是很好),自带的
- 下一篇: 1. CMake 系列 - 从零构建动态