利用 AWK 的數值計算功能提升工作效率

來源:互聯網
上載者:User

轉自IBM Developerworks

Awk 是一種優秀的文本樣式掃描和處理工具。本文側重介紹了 awk 在數值計算方面的運用,並通過幾個實際工作中的例子,闡述了如何利用 awk 的計算功能來提高我們的工作效率。

Awk 是一種優秀的文本樣式掃描和處理工具。 Awk 與 sed 和 grep 有些相似, 但功能比後者強不少。 awk 提供的功能包括樣式載入, 流量控制,數學運算子,進程式控制制以及許多內建的變數和函數等。 藉助於這些功能, 我們可以很方便地利用 awk 對各種檔案 (如實驗產生的資料檔案,資料庫檔案等) 進行處理。 本文介紹了 awk 在數值計算方面的運用, 並通過幾個實際的例子, 闡述了如何利用 awk 的計算功能來提高我們的工作效率。

 

Awk 基本的運算子,數學函數以及簡單的運算執行個體

Awk 支援不少常見的運算子, 如 + (加),- (減), * (乘), / (除), ^ 或 ** (乘方), % (模數) 等等。 此外, awk 也提供了一些常用的數學函數, 比如 sin(x), cos(x), exp(x), log(x), sqrt(x), rand()。 使用這些運算子和函數可以直接進行一些簡單的運算:

清單 1. 用 awk 做簡單的數值計算

echo | awk '{print 19+7}' ==> 26 echo | awk '{print 19-7}' ==> 12echo | awk '{print 19*7}' ==> 133echo | awk '{print 19/7}' ==> 2.71429echo | awk '{print 19**7}' ==> 893871739echo | awk '{print 19%7}' ==> 5echo | awk '{print atan2(19, 7)}' ==> 1.21781

上面的計算也可以用一個指令檔 calc.awk 來完成:

清單 2. 指令檔 calc.awk

{  print $1 " + " $2 " = " $1 + $2  print $1 " - " $2 " = " $1 - $2  print $1 " x " $2 " = " $1 * $2  print $1 " / " $2 " = " $1 / $2  print $1 " ^ " $2 " = " $1 ** $2  print $1 " mod " $2 " = " $1 % $2  print " atan2( " $1 " , " $2 " ) " " = " atan2($1, $2) }

執行 awk -f calc.awk 19 7 可以得到和清單 1 中一樣的計算結果。 這裡選項 -f 允許 awk 調用並執行程式檔案 calc.awk; 最後的 197 是輸入, 分別對應於檔案中的 $1$2

 

複雜一些的數值計算

現在我們利用 awk 來完成一些稍微複雜的計算。 我們首先用 awk 來計算 Fibonacci 數列,相應的 awk 程式 Fib.awk 見清單3:

清單 3. 計算 Fibonacci 數列的程式檔案

function fibo(n) {  if(n<=1) return 1;  return (fibo(n-2) + fibo(n-1)); } BEGIN {   n = (ARGV[1] < 1) ? 1 : ARGV[1];   printf("%d\n", fibo(n));   exit; }

計算時使用命令 awk -f Fib.awk n。 這裡的輸入 n 是整數。 另外只要把上面程式中的函數fibo(n) 稍微改一下, 就可以用來進行階乘的運算, 修改後的代碼如下:

清單 4. 計算價乘的 awk 指令碼

function factorial(n) {  if(n<=1) return 1;  return (n*factorial(n-1)); }BEGIN {   n = (ARGV[1] < 1) ? 1 : ARGV[1];   printf("%d\n", factorial(n));   exit; }

我們再來看一個求平方根的例子。 儘管 awk 提供了計算平方根的函數, 但我們也可以通過自己寫程式來實現, 相應的演算法如清單 5 所示; 清單 6 則給出了一個具體的例子: 求數字 3.7 的平方根:

清單 5. 求平方根的演算法


清單 6. 計算平方根的例子

BEGIN {   a = 3.7;   x = a; while((x**2-a)**2 > 1e-12) { x = (x + a/x)/2;}   print x }

執行個體1: 快速計算兩個檔案之間的時間差

如果僅僅從事單純的數值計算, 恐怕 awk 不是我們最好的選擇, 畢竟 awk 是為了方便文本處理而設計的。 不過如果數值計算和文本有密切關係的話, 比方說計算之前要先處理文本中的資料 (如尋找,提取資料), 這時 awk 的優勢就會充分顯示出來。 而這樣的情況在工作中往往是經常碰到的。 我們來看一個實際的例子。 假定我們要比較某些運行在 Linux 叢集上的並行程式的效率, 一個可行的方法是估算這些程式運行所需的時間。 這些程式啟動並執行時間通常比較長, 可以從 10 幾個小時到一個多星期。 注意到程式在運行中會不斷地產生資料檔案, 而 Linux 系統會紀錄下每個資料檔案被建立 (如果以前不存在) 或修改(如果以前存在) 的時間, 這樣就可以通過計算兩個檔案的時間差來估計並行程式的效率。 我們知道 Linux 提供的 stat 命令可以用來擷取某個檔案的各種屬性, 比如對資料檔案 simu_space_1.dat 使用命令 stat simu_space_1.dat 會有如下的輸出:

清單 7. 命令 stat simu_space_1.dat 的輸出

File:  "simu_space_1.dat"   Size: 237928    Blocks: 480        IO Block: 4096   regular DateiDevice: 801h/2049dInode: 2768915     Links: 1Access: (0644/-rw-r--r--)  Uid: ( 1000/     nst)   Gid: ( 1000/     nst)Access: 2008-11-14 10:56:05.000000000 +0100Modify: 2008-11-13 23:26:44.000000000 +0100Change: 2008-11-13 23:26:44.000000000 +0100

以上輸出包含了關鍵字 ’Modify’ 的一行中紀錄下了檔案被修改的時間。 所以原則上說只要對兩個檔案分別使用 stat 命令, 得到它們的修改時間, 就可以計算出它們之間的時間差。 如果計算的次數很少的話, 這個工作當然可以手工完成。 不過要頻繁計算的話就很費時間了, 而且出錯的幾率也會變大。 這種情況下我們可以求助 awk, 讓它來自動完成這個計算工作, 為此我們建立了下面的指令碼 time_df.awk:

清單 8. 計算時間差的 awk 程式

BEGIN {  n = 0;  d1 = 0;  s1 = 0;  FS = ":|-| *";}{ for(i=1; i<=NF; i++) {   if($i~/Modify/)   {    n = n + 1;    d = $(i+4);    h = $(i+5);    m = $(i+6);    s = $(i+7);    d1 = d1 + ((-1)**n)*d*24*3600;    s1 = s1 + ((-1)**n)*(3600*h + 60*m + s);   }   }}END {  s1 = s1 + d1;  D = int(s1/(24*3600));  H = int((s1 - D*24*3600)/3600);  M = int((s1-D*24*3600-H*3600)/60);  S = s1 % 60;  printf("The total time required %d days, %d hours,   \  %d minutes and %d seconds\n", D, H, M, S) ;}

上面的代碼是基於如下的考慮: 首先使用 awk 找到包含 ’Modify’ 關鍵字的那一行, 然後把其中有關日期和時間的資料提取出來。 由於直接對日期和時間做減法不是很方便, 所以先把日期和時間轉化為一個以秒為單位的數字 (從每個月的第一天0時0分0秒算起)。 容易理解, 由兩個數字相減得到的時間差也是以秒為單位的。 為了能直觀顯示, 輸出時再把這個時間差表達為天, 小時, 分鐘和秒。 要計算兩個檔案 simu_space_1.dat 和 simu_space_100.dat 之間的時間差,可以用下面的命令:

清單 9. 計算檔案時間差的命令

stat simu_space_1.dat simu_space_100.dat | awk -f time_df.awk

先產生的檔案 simu_space_1.dat (也就是時間較早一些的) 放在前面,後產生的檔案simu_space_100.dat 放在後面。 如果要算另外兩個檔案之間的時間差, 只要換一下檔案名稱就可以了。 藉助於上面的 awk 代碼我們可以快速且精確地得到任意兩個資料檔案的時間間隔。 需要指出的是, 上面的程式沒有考慮跨月度這種情況。 也就是說, 如果第一個資料檔案是在某個月的月末產生, 而第二個檔案是在下個月的月初產生, 這時就不能用它來計算, 因為得到的時間是沒有意義的負數。

 

執行個體2: 驗證通量:從多個檔案中提取資料並計算

這個例子是通過計算不同位置處的流體的通量來驗證它們是否相同。 這裡的通量可視為某個截面上通過的顆粒濃度, 流體的速度和截面面積的乘積。 現在的問題是濃度, 速度等參數分布在不同的資料檔案中, 而這些檔案是字元和資料共存的, 比如包含濃度的檔案 simu_space_1.dat 有如下的格式:

清單 10. 資料檔案 simu_space_1.dat 的格式

{0.436737, 0.429223, 3.000000, 1.000000, 43300806482080792.000000, 243231808.137785},

{1.296425, 0.429223, 3.000000, 1.000000, 107468809895964656.000000, 584622938.047805},

{2.128973, 0.429223, 3.000000, 1.000000, 102324821165926400.000000, 539067822.351442},

......

{19.358569, 4.875000, 3.000000, 1.000000, 257544788738191712.000000, 1460324590.999991},

{19.620925, 4.875000, 3.000000, 1.000000, 266676357086157504.000000, 1464352706.940682},

{19.875000, 4.875000, 3.000000, 1.000000, 260249342336872224.000000, 1383971975.659338},

第一步當然是從上面的檔案中把某個位置上的濃度資料 (每一行左起第五個數字) 提取出來。 下面的 awk 代碼是把位置 x = 0.429223 處的濃度提取出來, 並儲存到一個臨時檔案 number.txt 中:

清單 11. 提取指定位置處的資料並儲存

awk  -F'{|,\t|},' '{for(i=1; i<NF; i++) {if($i~/0.429223/) print $(i+3)}}'  \simu_space_1.dat > number.txt

現在檔案 number.txt 中有了一列濃度資料。 接著我們從其他檔案中提取在同一位置處的速度和面積的資料, 然後把它們分別儲存到臨時檔案 velocity.txt 和 area.txt 中。 然後把三個臨時檔案中的資料合併到另一個檔案 flux.txt 中以方便 awk 的計算。 這個合併作業可以用工具 paste 來輕鬆完成, 代碼如清單 12:

清單 12. 合并不同檔案中的資料到一個檔案

paste number.txt velocity.txt area.txt > flux.txt

現在 flux.txt 中包含了三列資料, 分別是濃度,速度和面積。 按照前面介紹的通量計算方法, 檔案 flux.txt 中每一行的三個資料要首先相乘, 然後再把所有的乘積加起來就可以得到通過那個截面的通量了, 具體的代碼見清單13:

清單 13. 計算通量的 awk 代碼

awk '{x=x+($1*$2*$3)} END {print x}' flux.txt

上面的代碼使用了一個變數 x, 第一次執行時, x 被賦予檔案 flux.txt 中第一行三個資料的乘積。 第二次執行時, 它保留了第一次計算的值並加上第二行三個資料的乘積, 以此類推, 直到達到累計的總合。 END 的作用是只顯示最後的結果, 而不顯示中間的累加結果。 我們可以做一個比較, 以前我們是利用其他的軟體 (比如 Excel 或者 OpenOffice Calc) 來計算通量的。 這必然要涉及到匯入資料, 選擇相應的計算函數等一系列的操作, 而用 awk 只要一行代碼! 如果再考慮到計算之前從不同檔案中提取資料的工作也是由 awk 完成的 (其實也就是幾行代碼), 所以對本例而言使用 awk 節約了可觀的時間。



回頁首

總結

不應忽視 awk 的數值計算功能, 它能完成從簡單到比較複雜的數值運算。 尤其當計算過程中涉及到資料檔案的處理, 這時使用 awk 往往會很方便。 因為 awk 本身有很強的文本處理功能, 它可以輕鬆地把資料從文本中分離出來, 然後再進行相應的計算。 本文的執行個體說明了如果能靈活地使用 awk 的這些功能, 有可能會顯著地提升我們的工作效率。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.