您好,登錄后才能下訂單哦!
awk:好用的數據處理工具
awk 也是一個非常棒的數據處理工具!sed 常常用于一整個行的處理, awk 則比較傾向于一行當中分成數個『欄位』(或者稱為一個域,也就是一列)來處理。因此,awk 相當的適合處理小型的數據數據處理呢!awk 通常運行的模式是這樣的:
[root@www ~]# awk '條件類型1{動作1} 條件類型2{動作2} ...' filename
awk 后面接兩個單引號并加上大括號 {} 來配置想要對數據進行的處理動作。 awk 可以處理后續接的文件,也可以讀取來自前個命令的 standard output 。 但如前面說的, awk 主要是處理『每一行的欄位內的數據』,而默認的『欄位的分隔符號為 "空白鍵" 或 "[tab]鍵" 』!舉例來說,我們用 last 可以將登陸者的數據取出來,結果如下所示:
[root@www ~]# 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)
若我想要取出帳號與登陸者的 IP ,且帳號與 IP 之間以 [tab] 隔開,則會變成這樣:
[root@www ~]# last -n 5 | awk '{print $1 "\t" $3}' root 192.168.1.100 root 192.168.1.100 root 192.168.1.100 dmtsai 192.168.1.100 root Fri
上表是 awk 最常使用的動作!透過 print 的功能將欄位數據列出來!欄位的分隔則以空白鍵或 [tab] 按鍵來隔開。 因為不論哪一行我都要處理,因此,就不需要有 "條件類型" 的限制!我所想要的是第一欄以及第三欄, 但是,第五行的內容怪怪的~這是因為數據格式的問題啊!所以羅~使用 awk 的時候,請先確認一下你的數據當中,如果是連續性的數據,請不要有空格或 [tab] 在內,否則,就會像這個例子這樣,會發生誤判喔!
另外,由上面這個例子你也會知道,在每一行的每個欄位都是有變量名稱的,那就是 $1, $2... 等變量名稱。以上面的例子來說, root 是 $1 ,因為他是第一欄嘛!至於 192.168.1.100 是第三欄, 所以他就是 $3 啦!后面以此類推~呵呵!還有個變量喔!那就是 $0 ,$0 代表『一整列數據』的意思~以上面的例子來說,第一行的 $0 代表的就是『root .... 』那一行啊! 由此可知,剛剛上面五行當中,整個 awk 的處理流程是:
讀入第一行,并將第一行的數據填入 $0, $1, $2.... 等變量當中;
依據 "條件類型" 的限制,判斷是否需要進行后面的 "動作";
做完所有的動作與條件類型;
若還有后續的『行』的數據,則重復上面 1~3 的步驟,直到所有的數據都讀完為止。
經過這樣的步驟,你會曉得, awk 是『以行為一次處理的單位』, 而『以欄位為最小的處理單位』。好了,那么 awk 怎么知道我到底這個數據有幾行?有幾欄呢?這就需要 awk 的內建變量的幫忙啦~
變量名稱 | 代表意義 |
NF | 每一行 ($0) 擁有的欄位總數 |
NR | 目前 awk 所處理的是『第幾行』數據 |
FS | 目前的分隔字節,默認是空白鍵 |
我們繼續以上面 last -n 5 的例子來做說明,如果我想要:
列出每一行的帳號(就是 $1);
列出目前處理的行數(就是 awk 內的 NR 變量)
并且說明,該行有多少欄位(就是 awk 內的 NF 變量)
則可以這樣:
Tips: 要注意喔,awk 后續的所有動作是以單引號『 ' 』括住的,由於單引號與雙引號都必須是成對的, 所以, awk 的格式內容如果想要以 print 列印時,記得非變量的文字部分,包含上一小節 printf 提到的格式中,都需要使用雙引號來定義出來喔!因為單引號已經是 awk 的命令固定用法了! |
[root@www ~]# last -n 5| awk '{print $1 "\t lines: " NR "\t columns: " NF}' root lines: 1 columns: 10 root lines: 2 columns: 10 root lines: 3 columns: 10 dmtsai lines: 4 columns: 10 root lines: 5 columns: 9 # 注意喔,在 awk 內的 NR, NF 等變量要用大寫,且不需要有錢字號 $ 啦!
這樣可以了解 NR 與 NF 的差別了吧?好了,底下來談一談所謂的 "條件類型" 了吧!
注:$0 表示整行,$1 代表第一項
awk 的邏輯運算字節
既然有需要用到 "條件" 的類別,自然就需要一些邏輯運算羅~例如底下這些:
運算單元 | 代表意義 |
> | 大於 |
< | 小於 |
>= | 大於或等於 |
<= | 小於或等於 |
== | 等於 |
!= | 不等於 |
值得注意的是那個『 == 』的符號,因為:
邏輯運算上面亦即所謂的大於、小於、等於等判斷式上面,習慣上是以『 == 』來表示;
如果是直接給予一個值,例如變量配置時,就直接使用 = 而已。
好了,我們實際來運用一下邏輯判斷吧!舉例來說,在 /etc/passwd 當中是以冒號 ":" 來作為欄位的分隔, 該文件中第一欄位為帳號,第三欄位則是 UID。那假設我要查閱,第三欄小於 10 以下的數據,并且僅列出帳號與第三欄, 那么可以這樣做:
[root@wuke ~]# cat /etc/passwd | \
> awk '{FS=":"}$3<10{print $1 "\t" $3}'
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
有趣吧!不過,怎么第一行沒有正確的顯示出來呢?這是因為我們讀入第一行的時候,那些變量 $1, $2... 默認還是以空白鍵為分隔的,所以雖然我們定義了 FS=":" 了, 但是卻僅能在第二行后才開始生效。那么怎么辦呢?我們可以預先配置 awk 的變量啊! 利用 BEGIN 這個關鍵字喔!這樣做:
[root@wuke ~]# cat /etc/passwd | \
> awk 'BEGIN{FS=":"}$3<10{print $1 "\t" $3}'
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
很有趣吧!而除了 BEGIN 之外,我們還有 END 呢!另外,如果要用 awk 來進行『計算功能』呢?以底下的例子來看, 假設我有一個薪資數據表檔名為 pay.txt ,內容是這樣的:
Name 1st 2nd 3th VBird 23000 24000 25000 DMTsai 21000 20000 23000 Bird2 43000 42000 41000
如何幫我計算每個人的總額呢?而且我還想要格式化輸出喔!我們可以這樣考慮:
第一行只是說明,所以第一行不要進行加總 (NR==1 時處理);
第二行以后就會有加總的情況出現 (NR>=2 以后處理)
[root@wuke shell]# cat pay.txt | \ > awk 'NR==1{printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"} > NR>=2{total=$2+$3+$4 > printf "%10s %10d %10d %10d %10.2f\n",$1,$2,$3,$4,total}' Name 1st 2nd 3th Total VBird 23000 24000 25000 72000.00 DMTsai 21000 20000 23000 64000.00 Bird2 43000 42000 41000 126000.00 上面的例子有幾個重要事項應該要先說明的: awk 的命令間隔:所有 awk 的動作,亦即在 {} 內的動作,如果有需要多個命令輔助時,可利用分號『;』間隔, 或者直接以 [Enter] 按鍵來隔開每個命令,例如上面的范例中,鳥哥共按了三次 [enter] 喔! 邏輯運算當中,如果是『等於』的情況,則務必使用兩個等號『==』! 格式化輸出時,在 printf 的格式配置當中,務必加上 \n ,才能進行分行! 與 bash shell 的變量不同,在 awk 當中,變量可以直接使用,不需加上 $ 符號。 利用 awk 這個玩意兒,就可以幫我們處理很多日常工作了呢!真是好用的很~ 此外, awk 的輸出格式當中,常常會以 printf 來輔助,所以, 最好你對 printf 也稍微熟悉一下比較好啦!另外, awk 的動作內 {} 也是支持 if (條件) 的喔! 舉例來說,上面的命令可以修訂成為這樣: [root@wuke shell]# cat pay.txt | \ > awk '{if(NR==1) printf "%10s %10s %10s %10s %10s\n",$1,$2,$3,$4,"Total"} > NR>=2{total=$2+$3+$4 > printf "%10s %10d %10d %10d %10.2f\n",$1,$2,$3,$4,total}' Name 1st 2nd 3th Total VBird 23000 24000 25000 72000.00 DMTsai 21000 20000 23000 64000.00 Bird2 43000 42000 41000 126000.00
你可以仔細的比對一下上面兩個輸入有啥不同~從中去了解兩種語法吧!我個人是比較傾向於使用第一種語法, 因為會比較有統一性啊! ^_^
學習了awk的基本知識,現在來做一些練習加深一下印象。
假設我們有這樣一個待處理的文件"grade.txt":
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansley 05/99 4712 Brown-2 12 30 28
#打印整個文件
[root@wuke shell]# awk '{print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansley 05/99 4712 Brown-2 12 30 28
#打印第一和第四個域
[root@wuke shell]# awk '{print $1,$4}' grade.txt
M.Tansley Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansley Brown-2
[root@wuke shell]# awk 'BEGIN{print "Name Belt\n-----------------"}
{print $1,$4}' grade.txt
Name Belt
-----------------
M.Tansley Green
J.Lulu green
P.Bunny Yellow
J.Troll Brown-3
L.Tansley Brown-2
正則表達式相關:
為使一域號匹配正則表達式,使用符號‘~’后緊跟正則表達式,也可以用 i f語句。awk中if后面的條件用()括起來。
#下面代碼打印$4 包含 Brown 的行
[root@wuke shell]# awk '$4~/Brown/{print $0}' grade.txt
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansley 05/99 4712 Brown-2 12 30 28
#非精確匹配
[root@wuke shell]# awk '$3~/48/{print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
#精確匹配
[root@wuke shell]# awk '$3=="48"{print $0}' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
#不匹配 使用 ‘!~’
[root@wuke shell]# awk '$0!~/Brown/' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
[root@wuke shell]# awk '$4!="Brown-2"{print$0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
#小于
[root@wuke shell]# awk '$6<$7{print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
#設置大小寫
[root@wuke shell]# awk '/[Gg]reen/' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
J.Lulu 06/99 48317 green 9 24 26
#匹配第一個域的第三個字符是‘a’
[root@wuke shell]# awk '$1~/^...a/' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
L.Tansley 05/99 4712 Brown-2 12 30 28
#'或'匹配,使用 ‘|’ ,需使用括號括起來
[root@wuke shell]# awk '$0~/(Yellow|Brown)/' grade.txt
P.Bunny 02/99 48 Yellow 12 35 28
J.Troll 07/99 4842 Brown-3 12 26 26
L.Tansley 05/99 4712 Brown-2 12 30 28
練習:寫一個腳本
1.設定變量file的值為/etc/passwd
2.使用循環讀取文件/etc/passwd的第2,4,6,10,13,15行,并顯示其內容
3.把這些行保存至/tmp/mypasswd文件中
#! /bin/bash
file="/etc/passwd"
if [ -e /tmp/passwd];then
b=0
else
touch /tmp/passwd
fi
for i in 2 4 6 10 13 15
do
exec 3>>/tmp/passwd
line=`head -$i $file|tail -1`
echo "$line"
echo "$line" >&3
exec 3>&-
done
****exec的重定向
先上我們進如/dev/fd/目錄下看一下:
root@localhost:~/test#cd /dev/fd
root@localhost:/dev/fd#ls
0 1 2 255
默認會有這四個項:0是標準輸入,默認是鍵盤。
1是標準輸出,默認是屏幕/dev/tty
2是標準錯誤,默認也是屏幕
255
當我們執行exec 3>test時:
root@localhost:/dev/fd#exec 3>/root/test/test
root@localhost:/dev/fd#ls
0 1 2 255 3
root@localhost:/dev/fd#
看到了吧,多了個3,也就是又增加了一個設備,這里也可以體會下linux設備即文件的理念。這時候fd3就相當于一個管道了,重定向到fd3中的文件會被寫在test中。關閉這個重定向可以用exec 3>&-。
root@localhost:/dev/fd#who >&3
root@localhost:/dev/fd#ls >&3
root@localhost:/dev/fd#exec 3>&-
root@localhost:/dev/fd#cat /root/test/te
test text
root@localhost:/dev/fd#cat /root/test/test
root tty1 2010-11-16 01:13
root pts/0 2010-11-15 22:01 (192.168.0.1)
root pts/2 2010-11-16 01:02 (192.168.0.1)
0
1
2
255
3
find是我們很常用的一個Linux命令,但是我們一般查找出來的并不僅僅是看看而已,還會有進一步的操作,這個時候exec的作用就顯現出來了。
exec解釋:
-exec 參數后面跟的是command命令,它的終止是以;為結束標志的,所以這句命令后面的分號是不可缺少的,考慮到各個系統中分號會有不同的意義,所以前面加反斜杠。
{} 花括號代表前面find查找出來的文件名。
使用find時,只要把想要的操作寫在一個文件里,就可以用exec來配合find查找,很方便的。在有些操作系統中只允許-exec選項執行諸如l s或ls -l這樣的命令。大多數用戶使用這一選項是為了查找舊文件并刪除它們。建議在真正執行rm命令刪除文件之前,最好先用ls命令看一下,確認它們是所要刪除的文件。 exec選項后面跟隨著所要執行的命令或腳本,然后是一對兒{ },一個空格和一個\,最后是一個分號。為了使用exec選項,必須要同時使用print選項。如果驗證一下find命令,會發現該命令只輸出從當前路徑起的相對路徑及文件名。
詳情請查看:http://www.cnblogs.com/peida/archive/2012/11/14/2769248.html
本文參考了一下好的文章:
http://www.cnblogs.com/zhuyp1015/archive/2012/07/11/2586985.html
http://www.cnblogs.com/peida/archive/2012/11/14/2769248.html
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。