Linux文字處理三劍客之awk
awk簡介
awk是一種解釋執行的程式語言,用來專門處理文字資料,其名稱是由它們設計者的名字縮寫而來 ———Afred Aho
,Peter Weinberger
與 Brian Kernighan
。常見版本有:
- awk
: 最原初的版本,它由 AT&T 實驗室開發
- nawk
:awk的改進增強版本
- gawk
:GNU awk,所有的 GNU/Linux 發行版都包括gawk,且完全相容awk與nawk
工作原理
awk -v FS=”:” -v OFS=”,” ‘{print
1, 3}’ /etc/passwd
1.awk對檔案或管道的內容一次只處理一行,將獲取搭配的這一行賦值給內部變數$0
2.將取到的一行的內容按awk內部變數FS定義的分割符(預設為空格,包含tab製表符,上面的例子通過-v
:
為分隔符)分為多個欄位,每一段儲存在3.awk中print命令列印欄位;{print
,
為輸出分隔符。接下來,awk處理下一行資料,直到所有的行處理完。
語法
awk [option] program FILE1...
program:PATTERN{ACTION STATEMENTS}
欄位說明:
option
:常用選項有-F
和-v
等
program
:匹配並操作文字
-{action}
:每一次輸入的行都會執行一次主體部分的命令
-PATTERN
:限定對於輸入的行的規則,可選
以下詳細說明awk的各個欄位…
用法
option
-F
:指明輸入時用到的欄位分隔符,預設為空白字元
-v var=value
:自定義變數
-f
:讀取檔案中的awk規則
awk -v str='hello' '{print str,$1}' file1
awk 'BEGIN{str="hello"}{print str,$1}' file1 # 效果同上
awk -f file1 # awk規則寫到指令碼中讀取
program
1.PATTERN
empty
:空模式,匹配每一行
/regular expression/
:僅處理能夠被此處的模式匹配到的行
relational expression
: 關係表示式;結果有“真”有“假”;結果為“真”才會被處理;真:結果為非0值,非空字串;
line ranges
:行範圍
startline,endline:/pat1/,/pat2/
#指明起始行結束行模式 note: 不支援直接給出數字的格式
BEGIN/END
模式
BEGIN
{}: 僅在開始處理檔案中的文字之前執行一次
END
{}:僅在文字處理完成之後執行一次
awk '/UUID/{print $1}' /etc/fstab #處理匹配到UUID的行
awk '!/UUID/{print $1}' /etc/fstab #處理UUID以外的行
awk -F : '$3<=500{print $1}' /etc/passwd #關係表示式匹配
awk -F : '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd #關係表達是用法
awk -F : '$NF~/bash$/{print $1,$NF}' /etc/passwd #用模式匹配符匹配
awk -F : '/root/,/ftp/{print $1,$NF}' /etc/passwd
awk -F: '(NR>=2&&NR<=10){print $1}' /etc/passwd
2.常用action
print
print item1, item2, ...
要點:
- 逗號分隔符;
- 輸出的各item可以字串,也可以是數值;當前記錄的欄位、變數或awk的表示式;
- 如省略item,相當於print $0;
awk '{print "line one\nline two\nline three"}'
printf
格式化輸出:printf FORMAT, item1, item2, ...
要點:
- FORMAT必須給出
- 不會自動換行,需要顯式給出換行控制符,\n
- FORMAT中需要分別為後面的每個item指定一個格式化符號
格式符:
%c
: 顯示字元的ASCII碼
%d, %i
: 顯示十進位制整數
%e, %E
: 科學計數法數值顯示
%f
:顯示為浮點數
%g, %G
:以科學計數法或浮點形式顯示數值
%s
:顯示字串
%u
:無符號整數
%%
: 顯示%自身修飾符:
#[.#]
:第一個數字控制顯示的寬度;第二個#表示小數點後的精度;
-
: 左對齊
+
:顯示數值的符號
awk -F: '{printf "%s\n",$1}' /etc/passwd
awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
awk -F : '{printf "Username:%15s\n",$1}' /etc/passwd #用修飾符控制輸出字元的寬度
awk -F : '{printf "Username:%15s\n",$1}' /etc/passwd #左對齊
操作符
算術操作符:X
+
,-
,*
,/
,%
,**
,^
Y (X^Y Y為X的次方)
賦值操作符:X=
,+=
,-=
,*=
,/=
,%=
,**=
,^=
, Y++
,--
比較操作符:X>
,>=
,<
,<=
,==
,!=
,~
,!~
Y
邏輯關係符:X&&
,||
Y
三元運算子:selector?
if-true-exp:
if-false-exp
#輸出系統使用者
awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser";printf "%15s:%-s\n",$1,usertype}' /etc/passwd
條件控制
- if-else: if (condition) {then-body} else {[ else-body ]}
awk -F: '{if ($1=="root") print $1, "Admin"; else print $1, "Common User"}' /etc/passwd
awk -F: '{if ($1=="root") printf "%-15s: %s\n", $1,"Admin"; else printf "%-15s: %s\n", $1, "Common User"}' /etc/passwd
awk -F: -v sum=0 '{if ($3>=500) sum++}END{print sum}' /etc/passwd
- while:while (condition){statement1; statment2; …}
awk -F: '{i=1;while (i<=3) {print $i;i++}}' /etc/passwd
awk -F: '{i=1;while (i<=NF) { if (length($i)>=4) {print $i}; i++ }}' /etc/passwd
- do-while:do {statement1, statement2, …} while (condition)
awk -F: '{i=1;do {print $i;i++}while(i<=3)}' /etc/passwd
- for:
for ( variable assignment; condition; iteration process) { statement1, statement2, ...}
for (i in array) {statement1, statement2, ...}
awk -F: '{for(i=1;i<=3;i++) print $i}' /etc/passwd
awk -F: '{for(i=1;i<=NF;i++) { if (length($i)>=4) {print $i}}}' /etc/passwd
awk -F: '$NF!~/^$/{BASH[$NF]++}END{for(A in BASH){printf "%15s:%i\n",A,BASH[A]}}' /etc/passwd
- case:
switch (expression) { case VALUE or /REGEXP/: statement1, statement2,... default: statement1, ...}
awk 'BEGIN{foo=0;switch(foo){case 0:print "yes";break; case 1:print "no";break; default: print "unknow"}}'
- next:提前結束對本行文字的處理,並接著處理下一行
awk -F: '{if($3%2==0) next;print $1,$3}' /etc/passwd # 顯示其ID號為奇數的使用者
awk高階應用
內建變數
$0
:匹配到的行的全部欄位
$1~$#
:匹配到行分割後的指定欄位
NF
:number of field,欄位個數
FS
:input field seperator,輸入的分隔符,預設為空白字元
OFS
:output field seperator,輸出的分隔符,預設為空白字元
RS
:input record seperator,輸入的換行符
ORS
:output record seperator,輸出時的換行符
NR
:number of record,當前記錄的欄位數,如果多檔案時編號累加
FNR
:與NR不同的是,FNR用於記錄正處理的行是當前這一檔案中被總共處理的行數
FILENAME
:awk命令所處理的檔案的名稱
陣列
array[index]
index可以使用任意字串,當某資料組元素不存在時,要引用其時,awk會自動建立此元素並初始化為空串,因此,要判斷某資料組中是否存在某元素,需要使用index in array的方式。
- 遍歷:for (var in array) { statement1, … }
- 刪除陣列變數:delete array[index]
#每出現一被/^tcp/模式匹配到的行,陣列S[$NF]就加1,NF為當前匹配到的行的最後一個欄位,此處用其值做為陣列S的元素索引;
netstat -ant | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
#統計某日誌檔案中IP地的訪問量
awk '{counts[$1]++}; END {for(url in counts) print counts[url], url}' /var/log/httpd/access_log
ARGC
:命令列中引數的個數,其awk命令也算一個引數
ARGV
:其是一個數組,儲存的是命令列所給定的各引數
awk '{print NF}' /etc/fstab # 每行欄位數,這裡是數量引用,不是對應的值引用
awk '{print $NF}' /etc/fstab # 每行中的最後一個欄位的值
awk '{print NR}' file1 file2 # 會每把所有文件進行總的編號,而不是單獨對檔案進行編號
awk '{print FNR}' file1 file2 # 會對每個檔案的行數進行單獨的編號顯示
awk '{print FILENAME}' file1 # 顯示當前檔名,但會每行顯示一次
awk 'END{print FILENAME}' file1 # 顯示當前檔名,但只會顯示一次
awk 'END{print ARGC}' /etc/fstab # 顯示共有幾個引數
內建函式
rand()
:隨機數,大於等於0,小於1
gsub(str1, str2, str)
:在str中將str1全部替換為str2,支援配符查詢
index(str1, str2 )
:在str1內找首次出現str2出現的位置,沒有則返回0
split(str,array[,fieldsep[,seps]])
:將str以fieldsep為分隔符分隔,並將結果儲存至array陣列中,下標為從0開始
length([str])
:返回string字串中字元的個數
substr(str, start [, length])
:擷取字串,從start開始,取length個,start從1開始計數
system(command)
:執行系統命令並將結果返回至awk
systime()
:系統當前時間戳
mktime("YYYY MM DD HH MM SS[ DST]")
:指定日期時間戳,注意加引號
strftime(format [,timestamp [,utc-flage]])
:生成指定格式時間,具體格式參考date命令
tolower(str)
:轉為小寫
toupper(str)
:轉為大寫
awk 'BEGIN{str="this is a test";split(str,A," ");print length(A);for(i in A){print i,A[i];}}'
awk 'BEGIN{str="str std str2";gsub("str","STR",str);print str}'
awk 'BEGIN{str="this is a test";b=match(str,"is");print b}'
awk 'BEGIN{str="this is a test2016";print substr(str,4,6)}'
netstat -ant | awk '/:80\>/{split($5,clients,":");IP[clients[1]]++}END{for(i in IP){print IP[i],i}}' | sort -rn | head -50
awk 'BEGIN{print mktime("2016 06 21 11 22 33")}'
awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S",systime())}'
awk '{system("mkdir " ($NF-1)}' filename # 倒數第二個欄位做為目錄名建立目錄
格式總是有問題,更好的閱讀體驗歡迎到訪
以上內容參考馬哥筆記。。。