• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Prayer

            在一般中尋求卓越
            posts - 1256, comments - 190, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

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

            Posted on 2010-03-11 21:17 Prayer 閱讀(387) 評論(0)  編輯 收藏 引用 所屬分類: Shell
            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}' ==> 12
                        echo | awk '{print 19*7}' ==> 133
                        echo | awk '{print 19/7}' ==> 2.71429
                        echo | awk '{print 19**7}' ==> 893871739
                        echo | awk '{print 19%7}' ==> 5
                        echo | 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 Datei
                        Device: 801h/2049d	Inode: 2768915     Links: 1
                        Access: (0644/-rw-r--r--)  Uid: ( 1000/     nst)   Gid: ( 1000/     nst)
                        Access: 2008-11-14 10:56:05.000000000 +0100
                        Modify: 2008-11-13 23:26:44.000000000 +0100
                        Change: 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 的這些功能, 有可能會顯著地提升我們的工作效率。



            參考資料



            關于作者

             

            在德國 Duisburg-Essen 大學, NST 工作. 對 Linux 和 open source software 很感興趣.

            国产精品成人久久久| 久久中文字幕精品| 久久精品午夜一区二区福利| 亚洲午夜久久久影院| 久久精品国产影库免费看| 久久亚洲2019中文字幕| 一本色道久久综合狠狠躁| 久久夜色精品国产欧美乱| 一本久久免费视频| 国产香蕉97碰碰久久人人| 久久精品国产99国产精品导航| 国产精品九九久久免费视频 | 久久免费的精品国产V∧| 国产亚洲精品美女久久久| 99精品国产综合久久久久五月天| 久久精品天天中文字幕人妻| 久久成人18免费网站| 97久久精品无码一区二区| 精品无码久久久久久尤物| 日韩va亚洲va欧美va久久| 久久久久九九精品影院| 国产 亚洲 欧美 另类 久久| 天天躁日日躁狠狠久久| 亚洲午夜久久久久久久久电影网| 国产高潮国产高潮久久久91| 久久久久久亚洲Av无码精品专口| 亚洲精品无码久久毛片| 久久久久久久综合日本| 精品国产一区二区三区久久| 久久亚洲欧美国产精品| 亚洲精品乱码久久久久久蜜桃不卡| 日本国产精品久久| 精品久久久久中文字| 精品欧美一区二区三区久久久| AV色综合久久天堂AV色综合在| 亚洲精品高清国产一线久久| 伊人 久久 精品| 久久强奷乱码老熟女网站| 伊人 久久 精品| 丁香色欲久久久久久综合网| 久久亚洲中文字幕精品一区|