awk
命令不僅提供了簡單的輸入字串篩選功能,還包含提取資料列、列印簡單文字、篩選內容——甚至做一些數學計算。
如果你僅使用 awk
選取一行中的特定文字,那麼你可能錯過了它的很多功能。在這篇文章中,我們會來看看使用 awk
可以幫你做一些其他的什麼事情,並提供一些例子。
awk
所提供的最簡單與最常用的功能便是從檔案或管道傳輸的資料中選取特定的內容。預設使用空格當做分隔符,這非常簡單。
$ echo one two three four five | awk ‘{print $4}’four$ who | awk ‘{print $1}’jdoefhenry
空格指的是一系列的 space
或 tab
字元。在下面所展示的命令裡,awk
從提供的資料中篩選第一和第四項。
awk
命令也可以通過在其後增加檔名引數的方式從文字檔案中獲取資料。
$ awk '{print $1,$5,$NF}' HelenKellerQuoteThe beautiful heart.
(LCTT 譯註:“The best and most beautiful things in the world can not be seen or even touched , they must be felt with heart.” ——海倫凱勒)
在這個例子中,awk
挑選了一行中的第一個、第五個和最後一個欄位。
命令中的 $NF
指定選取每行的最後一個欄位。這是因為 NF
代表一行中的欄位數量,也就是 23,而 $NF
就代表著那個欄位的值,也就是heart
。最後的句號也包含進去了,因為它是最後一個字串的一部分。
欄位能以任何有用的形式列印。在這個例子中,我們將欄位以日期的格式進行列印輸出。
$ date | awk '{print $4,$3,$2}'2019 Nov 22
如果你省略了 awk
命令中欄位指示符之間的逗號,輸出將會擠成一個字串。
$ date | awk '{print $4 $3 $2}'2019Nov21
如果你將通常使用的逗號替換為連字元,awk
就會嘗試將兩個欄位的值相減——或許這並不是你想要的。它不會將連字元插入到輸出結果中。相反地,它對輸出做了一些數學計算。
$ date | awk '{print $4-$3-$2}'1997
在這個例子中,它將年 “2019” 和日期 “22” 相減,並忽略了中間的 “Nov”。
如果你想要空格之外的字元作為輸出分隔符,你可以通過 OFS
(輸出分隔符)指定分隔符,就像這樣:
$ date | awk '{OFS="-"; print $4,$3,$2}'2019-Nov-22
你也可以使用 awk
簡單地顯示一些文字。當然了,比起 awk
你可能更想使用 echo
命令。但換句話說,作為 awk
指令碼的一部分,列印某些相關性文字將會非常實用。這裡有一個沒什麼用的例子:
$ awk 'BEGIN {print "Hello, World" }'Hello, World
下面的例子更加合理,新增一行文字標籤來更好的辨識資料。
$ who | awk 'BEGIN {print "Current logins:"} {print $1}'Current logins:shsnemo
不是所有的輸入都以空格作為分隔符的。如果你的文字通過其它的字元作為分隔符(例如:逗號、冒號、分號),你可以通過 -F
選項(輸入分隔符)告訴 awk
:
$ cat testfilea:b:c,d:e$ awk -F : '{print $2,$3}' testfileb c,d
下面是一個更加有用的例子——從冒號分隔的 /etc/passwd
檔案中獲取資料:
$ awk -F: '{print $1}' /etc/passwd | head -11rootdaemonbinsyssyncgamesmanlpmailnewsuucp
你也可以使用 awk
命令評估欄位。例如你僅僅想列出 /etc/passwd
中的使用者賬號,就可以對第三個欄位做一些篩選。下面的例子中我們只關注大於等於 1000 的 UID:
$ awk -F":" ' $3 >= 1000 ' /etc/passwdnobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologinshs:x:1000:1000:Sandra Henry-Stocker,,,:/home/shs:/bin/bashnemo:x:1001:1001:Nemo,,,:/home/nemo:/usr/bin/zshdory:x:1002:1002:Dory,,,:/home/dory:/bin/bash...
如果你想為輸出增加標題,可以新增 BEGIN
從句:
$ awk -F":" 'BEGIN {print "user accounts:"} $3 >= 1000 ' /etc/passwduser accounts:nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologinshs:x:1000:1000:Sandra Henry-Stocker,,,:/home/shs:/bin/bashnemo:x:1001:1001:Nemo,,,:/home/nemo:/usr/bin/zshdory:x:1002:1002:Dory,,,:/home/dory:/bin/bash
如果你想要不止一行的標題,你可以通過 "\n"
分隔輸出:
$ awk -F":" 'BEGIN {print "user accounts\n============="} $3 >= 1000 ' /etc/passwduser accounts=============nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologinshs:x:1000:1000:Sandra Henry-Stocker,,,:/home/shs:/bin/bashnemo:x:1001:1001:Nemo,,,:/home/nemo:/usr/bin/zshdory:x:1002:1002:Dory,,,:/home/dory:/bin/bash
awk
提供了驚人的數學計算能力,並且可以開平方,算 log
,算 tan
等等。
這裡有一對例子:
$ awk 'BEGIN {print sqrt(2019)}'44.9333$ awk 'BEGIN {print log(2019)}'7.61036
想要詳細了解 awk
的數學計算能力,可以看《使用 awk 進行數學計算》這篇文章。
你也可以使用 awk
寫一套單獨的指令碼。下面的例子模仿了之前寫過的一個,不過還計算了系統裡賬戶的數量。
#!/usr/bin/awk -f# 這一行是註釋BEGIN { printf "%s\n","User accounts:" print "==============" FS=":" n=0}# 現在開始遍歷資料{ if ($3 >= 1000) { print $1 n ++ }}END { print "==============" print n " accounts"}
注意 BEGIN
那一節是如何提供標題、指定欄位分隔符和初始化計數器的,它僅在指令碼初始化時期執行。這個指令碼也包含 END
節,它僅在中間所有命令處理完成之後執行,顯示了所有中間小節所篩選資料的最終行數(第三個欄位大於等於 1000)。
作為一個長存於 Unix 之上的命令,awk
依舊提供著非常有用的服務,這也是我幾十年前愛上 Unix 的原因之一。