溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Linux下的命令行編程工具Awk

發(fā)布時間:2021-09-27 11:03:01 來源:億速云 閱讀:96 作者:iii 欄目:系統(tǒng)運(yùn)維

這篇文章主要介紹“如何理解Linux下的命令行編程工具Awk”,在日常操作中,相信很多人在如何理解Linux下的命令行編程工具Awk問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何理解Linux下的命令行編程工具Awk”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

什么是Awk

Awk是一種小巧的編程語言及命令行工具。(其名稱得自于它的創(chuàng)始人Alfred Aho、Peter Weinberger 和 Brian Kernighan姓氏的首個字母)。它非常適合服務(wù)器上的日志處理,主要是因?yàn)锳wk可以對文件進(jìn)行操作,通常以可讀文本構(gòu)建行。
我說它適用于服務(wù)器是因?yàn)槿罩疚募?,轉(zhuǎn)儲文件(dump files),或者任意文本格式的服務(wù)器終止轉(zhuǎn)儲到磁盤都會變得很大,并且在每個服務(wù)器你都會擁有大量的這類文件。如果你經(jīng)歷過這樣的情境——在沒有像Splunk或者其他等價的工具情況下不得不在50個不同的服務(wù)器里分析幾G的文件,你會覺得去獲取和下載所有的這些文件并分析他們是一件很糟糕的事。

我親身經(jīng)歷過這種情境。當(dāng)一些Erlang節(jié)點(diǎn)將要死掉并留下一個700MB到4GB的崩潰轉(zhuǎn)儲文件(crash dump)時,或者當(dāng)我需要在一個小的個人服務(wù)器(叫做VPS)上快速瀏覽日志,查找一個常規(guī)模式時。

在任何情況下,Awk都不僅僅只是用來查找數(shù)據(jù)的(否則,grep或者ack已經(jīng)足夠使用了)——它同樣使你能夠處理數(shù)據(jù)并轉(zhuǎn)換數(shù)據(jù)。
代碼結(jié)構(gòu)

Awk腳本的代碼結(jié)構(gòu)很簡單,就是一系列的模式(pattern)和行為(action):   

代碼如下:

# comment
   Pattern1 { ACTIONS; }
   # comment
   Pattern2 { ACTIONS; }
   # comment
   Pattern3 { ACTIONS; }
   # comment
   Pattern4 { ACTIONS; }

掃描文檔的每一行時都必須與每一個模式進(jìn)行匹配比較,而且一次只匹配一個模式。那么,如果我給出一個包含以下內(nèi)容的文件:   

代碼如下:

this is line 1
   this is line 2

this is line 1 這行就會與Pattern1進(jìn)行匹配。如果匹配成功,就會執(zhí)行ACTIONS。然后this is line 1 會和Pattern2進(jìn)行匹配。如果匹配失敗,它就會跳到Pattern3進(jìn)行匹配,以此類推。

一旦所有的模式都匹配過了,this is line 2 就會以同樣的步驟進(jìn)行匹配。其他的行也一樣,直到讀取完整個文件。

簡而言之,這就是Awk的運(yùn)行模式
數(shù)據(jù)類型

Awk僅有兩個主要的數(shù)據(jù)類型:字符串和數(shù)字。即便如此,Awk的字符串和數(shù)字還可以相互轉(zhuǎn)換。字符串能夠被解釋為數(shù)字并把它的值轉(zhuǎn)換為數(shù)字值。如果字符串不包含數(shù)字,它就被轉(zhuǎn)換為0.

它們都可以在你代碼里的ACTIONS部分使用 = 操作符給變量賦值。我們可以在任意時刻、任意地方聲明和使用變量,也可以使用未初始化的變量,此時他們的默認(rèn)值是空字符串:“”。

最后,Awk有數(shù)組類型,并且它們是動態(tài)的一維關(guān)聯(lián)數(shù)組。它們的語法是這樣的:var[key] = value 。Awk可以模擬多維數(shù)組,但無論怎樣,這是一個大的技巧(big hack)。
模式

可以使用的模式分為三大類:正則表達(dá)式、布爾表達(dá)式和特殊模式。

正則表達(dá)式和布爾表達(dá)式

你使用的Awk正則表達(dá)式比較輕量。它們不是Awk下的PCRE(但是gawk可以支持該庫——這依賴于具體的實(shí)現(xiàn)!請使用 awk

–version查看),然而,對于大部分的使用需求已經(jīng)足夠了:   

代碼如下:

/admin/ { ... } # any line that contains 'admin'
   /^admin/ { ... } # lines that begin with 'admin'
   /admin$/ { ... } # lines that end with 'admin'
   /^[0-9.]+ / { ... } # lines beginning with series of numbers and periods
   /(POST|PUT|DELETE)/ # lines that contain specific HTTP verbs

注意,模式不能捕獲特定的組(groups)使它們在代碼的ACTIONS部分執(zhí)行。模式是專門匹配內(nèi)容的。

布爾表達(dá)式與PHP或者Javascript中的布爾表達(dá)式類似。特別的是,在awk中可以使用&&(“與”)、||(“或”)、!(“非”)操作符。你幾乎可以在所有類C語言中找到它們的蹤跡。它們可以對常規(guī)數(shù)據(jù)進(jìn)行操作。

與PHP和Javascript更相似的特性是比較操作符,==,它會進(jìn)行模糊匹配(fuzzy matching)。因此“23”字符串等于23,”23&Prime; == 23 表達(dá)式返回true。!= 操作符同樣在awk里使用,并且別忘了其他常見的操作符:>,<,>=,和<=。

你同樣可以混合使用它們:布爾表達(dá)式可以和常規(guī)表達(dá)式一起使用。 /admin/ || debug == true 這種用法是合法的,并且在遇到包含“admin”單詞的行或者debug變量等于true時該表達(dá)式就會匹配成功。

注意,如果你有一個特定的字符串或者變量要與正則表達(dá)式進(jìn)行匹配,~ 和!~ 就是你想要的操作符。 這樣使用它們:string ~ /regex/ 和 string !~ /regex/。

同樣要注意的是,所有的模式都只是可選的。一個包含以下內(nèi)容的Awk腳本:

代碼如下:

{ ACTIONS }

對輸入的每一行都將會簡單地執(zhí)行ACTIONS。
特殊的模式

在Awk里有一些特殊的模式,但不是很多。

第一個是BEGIN,它僅在所有的行都輸入到文件之前進(jìn)行匹配。這是你可以初始化你的腳本變量和所有種類的狀態(tài)的主要地方。

另外一個就是END。就像你可能已經(jīng)猜到的,它會在所有的輸入都被處理完后進(jìn)行匹配。這使你可以在退出前進(jìn)行清除工作和一些最后的輸出。

最后一類模式,要把它進(jìn)行歸類有點(diǎn)困難。它處于變量和特殊值之間,我們通常稱它們?yōu)橛颍‵ield)。而且名副其實(shí)。

使用直觀的例子能更好地解釋域:   

代碼如下:

# According to the following line
   #
   # $1 $2 $3
   # 00:34:23 GET /foo/bar.html
   # _____________ _____________/
   # $0
   
   # Hack attempt?
   /admin.html$/ && $2 == "DELETE" {
   print "Hacker Alert!";
   }

域(默認(rèn)地)由空格分隔。$0 域代表了一整行的字符串。 $1 域是第一塊字符串(在任何空格之前), $2 域是后一塊,以此類推。

一個有趣的事實(shí)(并且是在大多是情況下我們要避免的事情),你可以通過給相應(yīng)的域賦值來修改相應(yīng)的行。例如,如果你在一個塊里執(zhí)行 $0 = “HAHA THE LINE IS GONE”,那么現(xiàn)在下一個模式將會對修改后的行進(jìn)行操作而不是操作原始的行。其他的域變量都類似。
行為

這里有一堆可用的行為(possible actions),但是最常用和最有用的行為(以我的經(jīng)驗(yàn)來說)是:   

代碼如下:

{ print $0; } # prints $0. In this case, equivalent to 'print' alone
   { exit; } # ends the program
   { next; } # skips to the next line of input
   { a=$1; b=$0 } # variable assignment
   { c[$1] = $2 } # variable assignment (array)
   
   { if (BOOLEAN) { ACTION }
   else if (BOOLEAN) { ACTION }
   else { ACTION }
   }
   { for (i=1; i<x; i++) { ACTION } }
   { for (item in c) { ACTION } }

這些內(nèi)容將會成為你的Awk工具箱的主要工具,在你處理日志之類的文件時你可以隨意地使用它們。

Awk里的變量都是全局變量。無論你在給定的塊里定義什么變量,它對其他的塊都是可見的,甚至是對每一行都是可見的。這嚴(yán)重限制了你的Awk腳本大小,不然他們會造成不可維護(hù)的可怕結(jié)果。請編寫盡可能小的腳本。
函數(shù)

可以使用下面的語法來調(diào)用函數(shù):   

代碼如下:

{ somecall($2) }

這里有一些有限的內(nèi)置函數(shù)可以使用,所以我可以給出這些函數(shù)的通用文檔(regular documentation)。

用戶定義的函數(shù)同樣很簡單:   

代碼如下:

# function arguments are call-by-value
   function name(parameter-list) {
   ACTIONS; # same actions as usual
   }
   
   # return is a valid keyword
   function add1(val) {
   return val+1;
   }

特殊變量

除了常規(guī)變量(全局的,可以在任意地方使用),這里還有一系列特殊的變量,它們的的作用有點(diǎn)像配置條目(configuration entries):   

代碼如下:

BEGIN { # Can be modified by the user
   FS = ","; # Field Separator
   RS = "n"; # Record Separator (lines)
   OFS = " "; # Output Field Separator
   ORS = "n"; # Output Record Separator (lines)
   }
   { # Can't be modified by the user
   NF # Number of Fields in the current Record (line)
   NR # Number of Records seen so far
   ARGV / ARGC # Script Arguments
   }

我把可修改的變量放在BEGIN里,因?yàn)槲腋矚g在那重寫它們。但是這些變量的重寫可以放在腳本的任意地方然后在后面的行里生效。
示例

以上的就是Awk語言的核心內(nèi)容。我這里沒有大量的例子,因?yàn)槲亿呄蛴谑褂肁wk來完成快速的一次性任務(wù)。

不過我依然有一些隨身攜帶的腳本文件,用來處理一些事情和測試。我最喜歡的一個腳本是用來處理Erlang的崩潰轉(zhuǎn)儲文件,形如下面的:   

代碼如下:

=erl_crash_dump:0.3
   Tue Nov 18 02:52:44 2014
   Slogan: init terminating in do_boot ()
   System version: Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
   Compiled: Fri Sep 19 03:23:19 2014
   Taints:
   Atoms: 12167
   =memory
   total: 19012936
   processes: 4327912
   processes_used: 4319928
   system: 14685024
   atom: 339441
   atom_used: 331087
   binary: 1367680
   code: 8384804
   ets: 382552
   =hash_table:atom_tab
   size: 9643
   used: 6949
   ...
   =allocator:instr
   option m: false
   option s: false
   option t: false
   =proc:<0.0.0>
   State: Running
   Name: init
   Spawned as: otp_ring0:start/2
   Run queue: 0
   Spawned by: []
   Started: Tue Nov 18 02:52:35 2014
   Message queue length: 0
   Number of heap fragments: 0
   Heap fragment data: 0
   Link list: [<0.3.0>, <0.7.0>, <0.6.0>]
   Reductions: 29265
   Stack+heap: 1598
   OldHeap: 610
   Heap unused: 656
   OldHeap unused: 468
   Memory: 18584
   Program counter: 0x00007f42f9566200 (init:boot_loop/2 + 64)
   CP: 0x0000000000000000 (invalid)
   =proc:<0.3.0>
   State: Waiting
   ...
   =port:#Port<0.0>
   Slot: 0
   Connected: <0.3.0>
   Links: <0.3.0>
   Port controls linked-in driver: efile
   =port:#Port<0.14>
   Slot: 112
   Connected: <0.3.0>
   ...

產(chǎn)生下面的結(jié)果:   

代碼如下:

$ awk -f queue_fun.awk $PATH_TO_DUMP
   MESSAGE QUEUE LENGTH: CURRENT FUNCTION
   ======================================
   10641: io:wait_io_mon_reply/2
   12646: io:wait_io_mon_reply/2
   32991: io:wait_io_mon_reply/2
   2183837: io:wait_io_mon_reply/2
   730790: io:wait_io_mon_reply/2
   80194: io:wait_io_mon_reply/2
   ...

這是在Erlang進(jìn)程里運(yùn)行的函數(shù)列表,它們導(dǎo)致了mailboxe變得很龐大。腳本在這:   

代碼如下:

# Parse Erlang Crash Dumps and correlate mailbox size to the currently running
   # function.
   #
   # Once in the procs section of the dump, all processes are displayed with
   # =proc:<0.M.N> followed by a list of their attributes, which include the
   # message queue length and the program counter (what code is currently
   # executing).
   #
   # Run as:
   #
   # $ awk -v threshold=$THRESHOLD -f queue_fun.awk $CRASHDUMP
   #
   # Where $THRESHOLD is the smallest mailbox you want inspects. Default value
   # is 1000.
   BEGIN {
   if (threshold == "") {
   threshold = 1000 # default mailbox size
   }
   procs = 0 # are we in the =procs entries?
   print "MESSAGE QUEUE LENGTH: CURRENT FUNCTION"
   print "======================================"
   }
   
   # Only bother with the =proc: entries. Anything else is useless.
   procs == 0 && /^=proc/ { procs = 1 } # entering the =procs entries
   procs == 1 && /^=/ && !/^=proc/ { exit 0 } # we're done
   
   # Message queue length: 1210
   # 1 2 3 4
   /^Message queue length: / && $4 >= threshold { flag=1; ct=$4 }
   /^Message queue length: / && $4 < threshold { flag=0 }    
   # Program counter: 0x00007f5fb8cb2238 (io:wait_io_mon_reply/2 + 56)
   # 1 2 3 4 5 6
   flag == 1 && /^Program counter: / { print ct ":", substr($4,2) }

到此,關(guān)于“如何理解Linux下的命令行編程工具Awk”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI