與外界對話


Perl 本身已是一個相當強大的語言; 但是 perl 最大的力量來自於它與外界其他程式溝通的能力. 使用者本來就可以手動令你的 perl 程式與外界溝通, 例如想把目前目錄下所有檔案名稱的大小寫對調, 可以將 ls 的輸出餵給我們先前寫的 rencase 吃, 像這樣: ls | rencase 又如 count_arg 程式可以拿來數數看目前線上每個人 login 幾次, 像這樣: count_arg `who -q | grep -v '^#'` 但這需要使用我們程式的人懂得 pipeback quote 等觀念. 本單元要介紹的則是如何從你的 perl 程式 裡面 去執行外界的命令, 讓你的 perl 程式以四兩之力將千斤大問題撥給系統內其他程式去解決. 再收割成果呈現給使用者看, 讓不知情 (或不會自己下 pipe/back quote) 的使用者以為你有多厲害! (其實會發揮 組合的力量 的人, 真的很厲害.)

範例程式: 統計每位使用者上機次數與時間長短

四種基本方式

首先, 如果你只是想執行外部命令, 對這個外部命令產生的訊息並無興趣, 可以用 system 函數來執行外部命令:

        system("cp -r $ENV{HOME}/important $ENV{HOME}/backup");

當然, 你也可以用內建模組 File::Copy 做相同的事。

其次, 如果你不但要執行外部命令, 還希望在你的 perl 程式內分析它的結果, 那麼就用 back quote 來執行外部命令, 一口氣把它印出來的所有資料讀入一個陣列. 外部命令所印出來的每一列資料, 會依序存在陣列的每一個元素裡面:

        @x = `md5sum *.iso`;

但是上面的方法只適合用於資料量小, 或需要反複查詢的場合. 如果外部命令印出來的資料量太大, 全部讀入記憶體會很耗資源. 這時如果你的問題允許你循序每次只處理一列外部命令的輸出, 不需要經常回頭看過去的輸出, 那麼可以用第三種方法, 逐列處理:

        open F, "find $ENV{HOME} -name '*.*htm*' |";
        while (<F>) {
            ...
        }
        close F;

當然, 你也可以用內建模組 File::Find 做相同的事。

最後, 有時我們的 perl 程式所呼叫的外部命令是交談式的程式. 此時資料的流向顛倒過來, 變成是我們逐列送資料給外部程式. 還是用 open 的語法, 只不過 pipe 符號放的位置也顛倒過來:

        open F, "| gnuplot";
        print F "set term dumb\n";
        print F "plot sin(x)\n";
        close F;