1.命令簡介
AWK 是文本處理語言,是一個強大的文本分析工具,是 Unix/Linux 環境中功能強大的數據處理引擎之一。數據可以來自標準輸入(stdin)、一個或多個文件或其它命令的輸出。它支持用戶自定義函數和動態正則表達式等先進功能,是 Unix/Linux 下一個強大的編程工具。
AWK 有很多內建的功能,比如數組、函數等,這是它和 C 語言的相同之處,靈活性是 AWK 最大的優勢。簡單來說 AWK 就是把文件逐行的讀入,以空格和 Tab 為默認分隔符將每行切片,切開的部分再進行各種分析處理。
AWK 名稱來自于它的三位創始人 Alfred Aho、Peter Jay Weinberger 和 Brian Kernighan 姓氏的首個字母。AWK 有多個版本:awk, nawk, mawk 和 gawk,未作特別說明,一般指 gawk。gawk 是 AWK 的 GNU 版本。
2.命令格式
awk [OPTIONS]
awk [OPTIONS] ‘PATTERN{ACTION}’ FILE.。.
其中 PATTERN 一般為正則表達式,用斜杠括起來,用來查找匹配的行。ACTION 是在找到匹配的行時所執行的一系列命令。花括號 {} 對一系列指令進行功能分組,不需要始終出現。
盡管操作可能會很復雜,但語法總是這樣。awk 通常用來格式化文本文件中的信息,是以文件的行為處理單位,每接收文件的一行,然后執行相應的命令來處理文本。
注意:
(1)PATTERN 缺省為 1,表示永真,ACTION 缺省為 print。
(2)PATTERN + {ACTION} 可以同時存在多個,每個 PATTERN 之間的關系是或,只要當前行匹配 PATTERN,則執行 PATTERN 后大括號中的 action。
3.awk 的匹配模式
awk 的 PATTERN 可能是以下情況之一:
BEGIN
END
BEGINFILE
ENDFILE
/regular expression/
relational expression
pattern && pattern
pattern || pattern
! pattern
pattern ? pattern : pattern
(pattern)
pattern1, pattern2
BEGIN 和 END 是兩個特殊的模式,不會對輸入的內容進行測試。BEGIN 后的 action 在 awk 讀取文本前執行,END 后的 action 在 awk 結束前執行。模式表達式中的 BEGIN 和 END 模式不能與其他模式組合。
BEGINFILE 和 ENDFILE 是額外的兩個特殊模式,BEGINFILE 的 action 在讀取每個命令行輸入文件的第一條記錄之前執行,ENDFILE 的 action 在讀取每個文件的最后一條記錄之后執行。與 BEGIN 和 END 的區別是,如果給定多個文件,BEGINFILE 和 ENDFILE 的 action 將被執行多次,而 BEGIN 和 END 不管是否給定文件,其 action 只會執行一次。
/regular expression/ 表示正則表達式,用于選擇符合指定 pattern 的行。
relational expression 表示正則表達式的關系式,即多個正則表達式通過運算符進行組合。常見組合有:
pattern && pattern
邏輯與式,兩個 pattern 同時滿足才算滿足
pattern || pattern
邏輯或式,只要有一個 pattern 滿足即滿足
! pattern
邏輯非式,不符合 pattern 則為 true
pattern ? pattern : pattern
條件運算符式,第一個 pattern 滿足則判斷第二個 pattern,否則判斷第三個 pattern
(pattern)
括號用于改變 pattern 運算的優先級
pattern1, pattern2
表示一個范圍,用于選擇所有記錄行中第一個符合 pattern1 的記錄到下一個符合 pattern2 的記錄之間的記錄
4.選項說明
-C, --copyright
顯示版權信息并退出
-c, --traditional
是 awk 運行在兼容模式下,gawk 的任何擴展都不會生效
-d, --dump-variables[=FILE]
將 awk 排序后的全局變量的類型和值打印到指定的文件中,如果沒有指定 FILE,則在當前目錄默認生成一個 awkvars.out
-E, --exec FILE
功能類似于選項 -f,但腳本文件需要以 #! 開頭;另外命令行的變量將不再生效
-e, --source PROGRAM_TEXT
指定 awk 的源碼文件
-F, --field-separator FS
使用字符或字符串 FS 作為域分隔符。可以同時指定多個域分隔符,此時需要使用一對中括號括起來。例如使用-和|可寫作 -F ‘[-|]’。如果用[]作為分隔符,可寫作-F ‘[][]’。不指定分隔符,默認為空格和 Tab。注意,使用 -F‘ ’顯示指定空格時,Tab 也會被作為分隔符。使用 [] 指定多個分隔符時,又想使多個分隔符組成的字符串也作為分隔符,在 [] 后添加一個 +,如 -F“[ab]+”,那么分隔符有三個,a,b 和 ab
-f, --file PROGRAM_FILE
從指定的 awk 腳本文件 PROGRAM_FILE 讀取 awk 指令
-g, --gen-pot
解析 awk 程序,產生 .po(Portable Object Template) 格式的文件到標準輸出,來標明程序中每一個可本地化的字符串位置
-h, --help
顯示簡要的幫助信息并退出
-L, --lint[=VALUE]
打印有關在其它版本 awk 中出現可疑的或不可移植結構的警告。該選項提供了一個可選的參數 fatal,即將警告視為致命的錯誤
-m{f|r} VAL
-mf 將最大字段數設為 VAL;-mr 將最大記錄數設為 VAL。這兩個功能是 Bell 實驗室版awk 的擴展功能,在標準 awk 中不適用
-N, --use-lc-numeric
使用本地小數點解析輸入的數據
-n, --non-decimal-data
識別輸入數據中八進制和十六進制數
-O, --optimize
在程序的內部表示上啟用優化。目前,這只包括簡單的常量折疊。gawk 維護者希望隨著時間的推移增加額外的優化
-P, --posix
打開兼容模式,會出現以下限制:
不識別 x;
當域分隔符 FS 是一個空格時,只有空格和 Tab 能作為域分隔符,換行符將不能作為一個域分隔符;
在 ? 和 : 之后,不能繼續當前行;
函數關鍵字 func 將不能被識別;
操作符 ** 和 **= 不能代替 ^ 和 ^=;
fflush 函數無效。
-R, --command FILE
只限于 Dgawk。從文件中讀取調試器命令
-r, --re-interval
允許間隔正則表達式的使用。為默認選項
-S, --sandbox
在沙盒模式下運行gawk,禁用 system() 函數,使用 getline 進行輸入重定向,使用 print 和 printf 進行輸出重定向,以及加載動態擴展。命令執行也被禁用,這有效地阻止了腳本訪問本地資源
-t, --lint-old
打印關于不能向傳統 Unix awk 移植的構造的警告
--profile[=FILE]
輸出性能分析報告至指定的文件,默認輸出到 awkprof.out
-V, --version
打印版本信息并退出
-v, --assign VAR=VAL
定義一個 awk 變量并賦值,可以將外部變量傳遞給 awk
--
標識命令選項結束
5.awk 調用方式
有三種方式調用 awk。
(1)命令行方式。
awk [-F FS] ‘PATTERN + {ACTION}’ FILE.。.
在 awk 中,文件的每一行中,由域分隔符分開的每一項稱為一個域。通常,在不指明域分隔符的情況下,默認為空格和 Tab。
(2)Shell 腳本方式。
將所有的 awk 命令插入一個文件,腳本中在首行注明使用 awk 命令來解析執行,相當于將 Shell 腳本首行的#!/bin/sh換成#!/bin/awk,最后通過鍵入腳本名稱來調用。
(3)將所有的 awk 命令插入到一個單獨文件,然后使用 -f 選項調用。
awk -f awk-script-file FILE.。.
其中,-f 選項加載 awk-script-file 中的 awk 腳本,FILE… 跟上面的是一樣的。
6.awk 內置變量
gawk 有許多內置變量用來設置環境信息,這些變量可以被改變,下面給出常見的內置變量說明。
$0 當前處理行
$n 當前記錄的第 n 個字段,n 從 1 開始,字段間由 FS 分隔
ARGC 命令行參數個數
ARGIND 當前處理命令行中的第幾個文件,文件下標從 0 開始
ARGV 命令行參數數組
CONVFMT 數字轉換格式,默認值為%.6g
ENVIRON 支持隊列中系統環境變量的使用
ERRNO 最后一個系統錯誤的描述
FIELDWIDTHS 字段寬度列表(用空格鍵分隔)
FILENAME awk瀏覽的文件名
FNR 當前被處理文件的記錄數
FS 設置輸入域分隔符,等價于命令行-F選項
IGNORECASE 如果為真,則進行忽略大小寫的匹配
LINT 動態控制--lint選項是否生效,為false不生效,為true則生效;
NF 瀏覽記錄的域的個數
NR 已讀的記錄數
OFMT 數字的輸出格式,默認值是%.6g
OFS 輸出域分隔符
ORS 輸出記錄分隔符
RS The input record separator,輸入記錄的分隔符,默認為換行符
RT The record terminator,輸入記錄的結束符
RSTART 由 match 函數所匹配的字符串的第一個位置
RLENGTH 由 match 函數所匹配的字符串的長度
SUBSEP 數組下標分隔符(默認值是 34)
TEXTDOMAIN awk 程序所使用的文本所處的地域
7.awk 編程示例
7.1 基礎打印輸出
(1)假設 last -n 5 的輸出如下:
root pts/1 192.168.1.100 Tue Feb 10 11:21 still logged in
root 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 Fri Sep 5 14:09 - 14:10 (00:01)
如果只是顯示最近登錄的5個帳號:
last -n 5 | awk ‘{print $1}’
root
root
root
dmtsai
root
awk 工作流程是這樣的:讀入有換行符分隔的一條記錄,然后將記錄按指定的域分隔符劃分,$0則表示所有域,$1表示第一個域,$n表示第 n 個域。默認域分隔符是空格或 Tab 符,所以$1表示登錄用戶,$3表示登錄用戶 ip,以此類推。
(2)如果想顯示 /etc/passwd 配置文件中的賬戶以及賬戶對應的 Shell,而賬戶與 Shell 之間以Tab符分隔。
cat /etc/passwd |awk -F ‘:’ ‘{print $1“ ”$7}’
root /bin/bash
daemon /bin/sh
bin /bin/sh
sys /bin/sh
注意,這里使用了 -F 指定域分隔符為冒號 :。
(3)如果只是顯示 /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操作。
(4)搜索 /etc/passwd 有 root 關鍵字的所有行。
awk -F: ‘/root/’ /etc/passwd
root0root:/root:/bin/bash
上面三種是 awk 的 action 的使用示例,而這種是 pattern 的使用示例,匹配了 pattern(這里是root)的行才會執行 action(沒有指定 action,默認輸出每行的內容)。
搜索支持正則表達式,例如找 root 開頭的所有行。
awk -F: ‘/^root/’ /etc/passwd
(5)搜索/etc/passwd有 root 關鍵字的所有行,并顯示對應的 Shell。
awk -F: ‘/root/{print $7}’ /etc/passwd
/bin/bash
這里是 awk 的 pattern+action 示例用法,同時指明了 action 是 {print $7}。
(6)打印 /etc/passwd 第三行的第一列和第二列。
awk -F: ‘NR==3{print $1,$2;}’ /etc/passwd
#輸出結果:
daemon x
7.2 awk 在每一列后添加字符串后輸出
設定變量內容:
a=“/test.html /dir1 /abc.txt”
希望得到
echo $a
--exclude=/test.html --exclude=/dir1 --exclude=/abc.txt
如何用 awk 實現。
解決辦法:
echo $a|awk ‘{for(i=1;i《=NF-1;++i){printf “-execute=%s ”,$i}}{print “--exclude=”$NF“”}’
#或者
echo $a|awk ‘{for(i=1;i《=NF;i++){printf “--exclude=”$i“ ”}{print “”}}’
后者是網友給出的答案,和我上面的寫法差不多,只是對 printf 在使用形式上有所差別而已。第二種方法print “”用于換行,print 每次輸出后默認進行換行。
7.3 Shell 編程使用 awk 浮點運算保留兩位小數
a=3
b=10
c=$(awk ‘BEGIN{printf “%.2f”,’$a‘*100/’$b‘}’)
echo c:$c%
或者:
c=$(awk -v n=$a -v m=$b ‘BEGIN{printf “%.2f”,n*100/m}’)
echo c:$c%
-v表示定義awk的變量!v是variable的首字母。輸出:c:30.00%。
7.4 awk 訪問 Shell 變量
awk 默認是無法訪問shell變量的,我所知道的有三種方法。
方法一:awk -v 選項讓awk 里使用shell變量。
var0=dablelv0
var1=dablelv1
awk -v tmpVar0=$var0 -v tmpVar1=$var1 ‘BEGIN{print tmpVar0“ ”tmpVar1}’
輸出:dablelv0 dablelv1
注意:BEGIN 必須大寫,awk 的 {action} 必須要使用單引號括起來。
方法二:‘“$var”’
這種寫法是老外常用的寫法。如:
var=“test”
awk ‘BEGIN{print “’$var‘”}’
這種寫法其實際是雙括號變為單括號的常量,傳遞給了awk。
如果var中含空格,為了shell不把空格作為分格符,應該如下使用:
var=“this is a test”
awk ‘BEGIN{print “’”$var“‘”}’
方法三:export 變量,將變量設置為臨時會話環境變量,僅在當前shell會話中有效。在awk中使用ENVIRON[“var”]形式訪問變量。
var=“this is a test”
export $var #或者 export var
#或者
export var=“this is a test”
awk ‘BEGIN{print ENVIRON[“var”]}’
7.5 awk 執行 Shell 命令
awk 執行 Shell 命令有兩種方法。
方法一:使用awk的system()函數。
export var=dablelv
awk ‘BEGIN{system(“echo $var”)}’
輸出:dablelv
注意:一定要使用export將變量設置為臨時環境變量,因為awk的system()實際上是新建了一個shell進程來執行給定的shell命令,否則無法訪問父進程的變量。
方法二:使用使用print cmd | “/bin/bash”
var=“this is a test”
awk ‘BEGIN{print “echo ”“’”$var“‘”|“sh”}’
#或者
var=“this is a test”
awk -v varTmp=“$var” ‘BEGIN{print “echo ”varTmp|“sh”}’
輸出:
this is a test
注意:
(1)指定bash的時候需要雙引號括起來;
(2)方法二與方法一的區別在于方法二是將變量在awk解析后再通過管道傳給shell,所以無需將變量設置為臨時環境變量,因為shell接收到的變量已經是變量的值。
8.awk 常見問題
(1)awk 默認以空格和 Tab 作為域分隔符,現在只以空格為分隔符,需要使用中括號的方式,不使用中括號,則仍然會將 Tab 作為域分隔符。
# 錯誤的寫法
awk -F‘ ’ ‘{print $1;}’ test.txt
# 正確的寫法
awk -F‘[ ]’ ‘{print $1;}’ test.txt
原文標題:每天一個 Linux 命令(132):awk 命令
文章出處:【微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。
責任編輯:haq
-
Linux
+關注
關注
87文章
11230瀏覽量
208932
原文標題:每天一個 Linux 命令(132):awk 命令
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論