即時餵資料給程式吃 - 朝陽科技大學

文章推薦指數: 80 %
投票人數:10人

Perl 會把使用者敲在命令列上的參數放在@ARGV 陣列裡。

(熟悉C 語言的讀者請注意: 在perl 當中, $ARGV[0] 是第一個參數, 不是命令名稱) 程式執行方式(以count_arg 為例): Perl語言 新手上路 基本要素 餵資料 常用句型 regexp 詳談變數 一語中的 副程式 模組 外界對話 附錄 參考資料 scripting Windows 圖形介面 big-5碼 light dark 我的部落格: 人權 玩具 快速跳到: 社群活動 本層目錄 上層目錄 此頁@朝陽 此頁@資管 English 即時餵資料給程式吃 範例程式 以下有三個範例程式,每個程式各有兩個版本。

兩版本功能完全相同, 只是讀取輸入資料的方式不一樣--「命令列參數版」 從命令列參數讀取資料;「癡癡地等版」則是在使用者下了命令,按了 enter之後,癡癡地等著使用者逐列輸入資料。

---------------------------------------------------------------------- 左半是「命令列參數版」中間是註解右半是「癡癡地等版」 ---------------------------------------------------------------------- #!/usr/bin/perl-w#!/usr/bin/perl-w #名稱:length_arg#名稱:length_stdin #功用:數每個輸入字串的長度#功用:數每個輸入字串的長度 usestrict; my( $str,#每個字串 ); foreach$str(@ARGV){#對每個字串..while(){ printlength($str),"\n";#印出其長度printlength($_),"\n"; }} ---------------------------------------------------------------------- #!/usr/bin/perl-w#!/usr/bin/perl-w #名稱:sum_arg#名稱:sum_stdin #功用:對所有輸入數字加總#功用:對所有輸入數字加總 usestrict;usestrict; my(my( $number,#每個值 $sum#總和$sum );); foreach$number(@ARGV){#對每個數字..while(){ $sum+=$number;#累加$sum+=$_; }} print"$sum\n";print"$sum\n"; ---------------------------------------------------------------------- #!/usr/bin/perl-w#!/usr/bin/perl-w #名稱:count_arg#名稱:count_stdin #功用:統計每個字出現幾次#功用:統計每個字出現幾次 usestrict;usestrict; my(my( $str,#每個字串$str, %freq#出現頻率表%freq );); foreach$str(@ARGV){#對每個字串..while(){ #(砍列尾\n)chomp$_; ++$freq{$str};#又多了一次!++$freq{$_}; }} foreach$str(keys%freq){foreach$str(keys%freq){ print"$freq{$str}$str\n";print"$freq{$str}$str\n"; }} ---------------------------------------------------------------------- 又請參考「命令列參數」版與 「癡癡地等」版的選擇排序程式。

兩種常用的資料輸入方式 為了讓我們寫的程式用起來更有彈性, 我們不喜歡把運算要用到的的所有輸入資料(例如要把那些數字拿來加總?) 寫死在程式裡,而是留給使用者在執行程式時再臨時決定。

這類「要執行程式時才決定的資料」,有兩種簡單的方法傳給程式。

其一是直接讀取命令列上,命令名稱後面的參數commandline argument(如圖左半部,想像資料從方框的上面進來)。

Perl 會把使用者敲在命令列上的參數放在@ARGV陣列裡。

(熟悉C 語言的讀者請注意:在perl當中,$ARGV[0]是第一個參數, 不是命令名稱)程式執行方式(以count_arg為例): ./count_argapplebananaorange...pineapplepeach 若使用者下這個命令來執行我們的程式,我們在count_arg 程式內會發現$ARGV[0]裡面存著"apple",$ARGV[1]裡面存著"banana", ...Q:如何知道使用者在命令列上究竟敲入多少個參數?又, 請印出最後一個參數。

第二種方法是從standardinput標準輸入裝置讀入資料: 在使用者打完命令,按了enter之後, 我們的程式才癡癡地等著他從鍵盤上敲入資料,每列一筆。

(如圖右半部, 想像資料從方框的左邊進來)程式語法是while(){...} 如此perl會每次(即迴圈裡面每執行一回之前) 讀入使用者輸入資料的一列,並且固定將它放在$_ 這個神奇的變數裡讓我們的程式用。

注意每列最後面有一個換列字元, 習慣上以chomp把它刪除。

程式執行方式(以count_stdin為例): ./count_stdin (提示符號未出現,但其實並未當掉.使用者開始輸入資料) apple banana orange ... pineapple peach ^d(使用者按著Ctrl鍵不放,再按一下d鍵,表示資料輸入完畢) (在Windows下則是按^z,不按^d) 第二種做法看起來有點無厘頭("連個提示符號都沒有, 害我以為程式掛掉了!")但其實在某些場合比第一種方法方便-- 因為有一些方法可以欺騙程式,讓它把其他東西當做是 「使用者從鍵盤上輸入的資料」,而所謂的「其他東西」, 未必需要真的辛苦地人工敲入!Shell 提供四種特殊符號讓我們改變資料的流向,方便我們欺騙程式。

首先,程式原本想要印到螢幕上的東西, 我們可以將它轉而存到一個檔案裡面,像這樣:length_argapple bananaorange...pineapplepeach>len.txt這叫做 outputredirection輸出重新導向。

也可以說它的 standardoutput標準輸出裝置被重新導向了。

看到 >符號時, 你可以在心中想像右邊出去的管線被改灌入地下。

其次,程式原本想要從鍵盤上讀入的東西, 我們可以轉而從一個既存的檔案裡面抓出來餵它吃,像這樣: sum_stdin ans 請圖示以下指令,並預測會產生何種結果:./length_arg $(catfruit.txt)|./count_stdin| ./sum_stdin 請換一種執行方式,簡化這個命令:length_argapplebanana orange...pineapplepeach|sum_stdin 不要辛苦地自己敲入所有水果名稱,改令程式從檔案中讀出。

(一種方式是改用length_stdin;另一種方式是仍舊使用 length_arg...) 注意:脫離while迴圈單獨使用的狀況比較複雜, 目前請固定將它放在while迴圈當中使用。

Q:與perl無關的題外話:為什麼以下執行方式, 前三者得到相同的結果;後二者得到相同的結果?提示:這些引號不是給 perl看的,而是給另外一個程式看的。

在你的perl程式接手之前, 它就已經視需要將引號剝掉了。

length_arggoodmorning length_arg"good""morning" length_arg'good''morning' length_arg"goodmorning" length_arg'goodmorning' Q:length_arga.txtd.txt e.txt請問這句話,你的perl程式length看到的@ARGV 裡面有幾個元素?結果會發生什麼事? 選擇含有特定字串的那幾列 下面這個程式pgrep從標準輸入裝置讀取資料, 然後只將符合搜尋條件的那幾列印出來。

所謂符合搜尋條件, 就是這一列上包含指定的字串。

(用命令列參數指定)例如下 ./pgrep'200'就會將STDIN輸入的資料當中,含有' 200'子字串的那幾列印出來。

#!/usr/bin/perl-w die"usage:pgreppattern"unless$#ARGV>=0; while(){ nextunless$_=~/$ARGV[0]/; print$_; } 這裡的nextunless...如果一下腦筋轉不過來, 可以改寫兩次,變成比較簡單直接的語法,就比較容易懂了:先改成 unless(...){next;}再改成if(not...){ next;} 這個程式其實近乎等同於shell底下的grep。

它甚至可以拿來搜尋複雜的字串,例如./pgrep 's9214[01].[13579]'印出所有包含"四技二A同學學號" 的那幾列。

這些表達複雜字串規則的特殊符號叫做RegularExpressions。

取得每列第k個欄位 下面這個程式get_field假設標準輸入裝置輸入的每列資料, 都以冒號分隔所有欄位,像這樣: root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/bin/sh ... ckhung:x:1006:100:洪朝貴,T2.907,x4271:/home/ckhung:/bin/bash (順便一提:這是/etc/passwd檔的內容,別人finger你的時候, 系統就是在這裡找到你的個人資料。

如果你希望別人finger你的時候, 秀出比較酷的資訊,可以下chfn指令。

又,題外話: 系統其實已經有一個類似的指令,請見cut(1)) get_field會抓出第1個欄位的資料。

(注意欄位號碼從第0 個欄位數起。

)又,如果你想抓其他欄位的話, 可以在命令列上放一個數字當做參數,像這樣:./get_field 4,則它會抓出第4個欄位,也就是"root","bin",... "洪朝貴,T2.907,x4271"等等。

#!/usr/bin/perl-w usestrict; my($which,$i,$n,$c); $which=($#ARGV>=0)?$ARGV[0]:1; while(){ chomp$_; $n=0;#到目前為止已經看到幾個冒號了? for($i=0;$i$which); }else{ print$cif($n==$which); } } print"\n"; } 這整個程式其實可以用一句話來取代。

例如./get_field 4可以用perl-ne'print((split/:/)[4],"\n")' /etc/passwd來取代。

常用運算子 邏輯運算子andornot&&||!:Perl 的邏輯運算規則與C相同--都是採取short-circuit evaluation,如果一長串andor的前半部算完時, 已經可以知道答案,它就會偷懶地省略計算後半部的工作。

例如if ($candies/$children<5and$children>2){...}有 bug(萬一要是$children等於零怎麼辦?);如果改成if ($children>2and$candies/$children<5){...} 就沒有問題了。

處理字串的運算子: .可以連接字串,例如"concate"."nation" 的結果是"concatenation" x可以複製字串,例如"Great!"x3的結果是 "Great!Great!Great!" 比較字串用lt(lessthan),le(lessequal),gt(greater than),ge(greaterequal),eq(equal),ne(notequal). 搜尋子字串$x=~/.../ 更詳細的討論,請見perlop(1)(<==這個的意思是下 man1perlop) 簡單流程控制 與一般語言一樣,perl提供以下幾個一看就懂的流程控制敘述: if(EXPR){...} if(EXPR){...}else{...} if(EXPR){...}elsif(EXPR){...}elsif(EXPR){ ...}...else{...} while(EXPR){...} 比較特別的是unless(EXPR){...}"除非"。

如果頭昏,可以將它翻譯成if(not(EXPR)){... }。

其中的EXPR是判斷條件,例如$i$min等等。

熟悉C語言的讀者請注意: 即使大括弧內只有一句話,大括弧還是不能省略。

上述所有句形都一樣。

另外還有先前學過的for,其實可以用while來模擬;foreach可以用 for來模擬. 有時需要打斷迴圈正常流程: next這個迴圈的這一次剩下的部分不做了, 直接進入迴圈的下一次.(類似C語言中的continue) last完全跳出這個迴圈, 不但這一次剩下的部分不做了,不論剩下多少次都不做了.(類似C 語言中的break) 這兩個命令還可以跳出外層的迴圈,不像C的continue與break, 只限於跳出最內層的迴圈.詳見perlsyn(1)中談論迴圈的label 的部分. Perl裡面比較特別的是"英文式"寫法:在英文裡, 條件子句可以寫在主要子句前面,而意思其實沒有變。

例如:"Takethe lefttrailifyouhearadogbarks."在perl裡面, 如果主要子句只有短短一句話,那麼也可以這樣寫.例如: print$iif$i>0;#$i大於零才要印 print$iunless$i>0;#$i大於零就不印;其他情況都印 更詳細的討論,請見perlsyn(1)(<==這個的意思是下 man1perlsyn) 其他常識 遇到沒有看過的函數,可查手冊perlfunc(1)或perlop(1) 前面那篇是perl函數手冊;後面這篇是perl運算子手冊。

因為在 perl裡面,有些看起來像函數的東西其實是運算子,例如eq,x,cmp, and,...等等。

比方說要查perl內的printf怎麼用,可以:下man 1perlfunc命令,然後打:斜線"/",乘方"^",七個空格,"printf", 然後按Enter。

見regularexpression 範例) 一個變數,如果未曾設定初始值,那麼它的初始值就是undef. undef值出現在數學運算式當中時,perl把它當做0來看待; 出現在字串運算式當中,perl把它當做空字串來看待. 三元運算子(測試)?(成功值):(失敗值)先判斷 (測試)是否成立,若成立,則傳回(成功值),不然就傳回 (失敗值) substr函數可以取出字串的一小段。

(指定從第幾個字元開始取, 總共要取幾個字元)注意:字元從第0個開始數起。

以後學split 函數或regularexpression程式可以簡潔許多, 你會發覺幾乎不太需要用到substr。

這個單元裡面的兩個小程式合起來,其實就已經可以做到這當中的"分析流量"一例的前半部: ./get_field



請為這篇文章評分?