誰常上機?


產生統計資料

I/O redirection 圖示

last 指令可以查詢最近一段時間, 誰曾經登入系統。 今天我們的任務是統計一下所有人使用這部主機的頻率與時間, 並畫成圖表。

第一部分我們先只統計每人登入的次數。

  1. last 看一下系統總共有多少人次的登入記錄?
  2. 我們只對本班同學有興趣: last | perl -ne 'print if /^s4113/'
  3. 把這部分的結果存起來: last | perl -ne 'print if /^s4113/' > lastlog.txt
  4. 我們只對 user name 欄位有興趣: perl -pe 's/ .*//' < lastlog.txt
  5. 排序一下, 把同一位使用者的資料集中在一起: perl -pe 's/ .*//' < lastlog.txt | sort
  6. 再數一下每個人出現的次數: perl -pe 's/ .*//' < lastlog.txt | sort | uniq -c
  7. 按照出現頻率排序, 由多到少: perl -pe 's/ .*//' < lastlog.txt | sort | uniq -c | sort -nr
  8. 取前廿名: perl -pe 's/ .*//' < lastlog.txt | sort | uniq -c | sort -nr | head -20
  9. 將最後結果存起來: perl -pe 's/ .*//' < lastlog.txt | sort | uniq -c | sort -nr | head -20 > freq.txt 產生類似這樣的 freq.txt

產生 freq.txt 的過程

這裡的 "|" 叫做 pipe, 作用是將前一個指令的輸出, 餵給下一個指令當它的輸入。 如果前一個指令本來就想印到 standard output 標準輸出裝置, 後一個指令本來就想從 standard input 標準輸入裝置 讀資料, 那麼就可以用 pipe 連接起來。 用白話文講, 如果前一個指令原本要印到螢幕上; 後一個指令原本要 "癡癡地等" 從鍵盤讀資料, 那麼 pipe 的作用正好讓前者的輸出直接變成後者的輸入。 last | perl -pe 's/ .*//' 的效果跟 last > lastlog.txt ; perl -pe 's/ .*//' < lastlog.txt 兩句合起來的效果一樣; 只不過後者會多產生一個中間過程檔案。 (註: perl -ne 'print if /^s4113/'grep '^s4113' 的效果一樣; 而 perl -pe 's/ .*//'sed 's/ .*//' 的效果一樣。 圖中採用簡寫版本。)

製作圖表

我們將使用 gnuplot 製作圖表。 打 gnuplot 之後, 進入它的環境, 現在開始與我們交談的程式不再是 bash, 而是 gnuplot。 使用 knoppix 光碟或是從 Windows 下用 XLiveCD 連線到學校主機的同學, 應該會看到 "Terminal type set to 'x11'" 的訊息。 從 Windows 下用 telnet 或 putty 連線的同學, 看到的不是 x11 而是 unknown, 這時只好下 set term dumb 用笨笨的終端機模式畫圖。

gnuplot 跟其他許多文字模式的交談式程式一樣, 也是按 ^d 離開。 先畫幾個簡單的圖, 熟悉一下 gnuplot 的命令:

  1. plot x*x-5
  2. plot sin(x)/x
  3. splot x*x-y*y
  4. plot "freq.txt"
  5. plot "freq.txt" with boxes
  6. help plot (這篇 help 很長, 裡面有很多子題 subtopcs, 閱讀時請留意下方的訊息。)

我們希望在 x 軸下方標示出 username。

  1. 先隨便亂實驗一下: set xtics ("abc" 0, "def" 1, "xyz" 2)
  2. 要重畫時, 標示才會出現: replot
  3. 轉 90 度, 字才不會疊在一起: set xtics rotate ("abc" 0, "def" 1, "xyz" 2); relpot
  4. (較新版本的 gnuplot) 如果覺得左下角的滑鼠座標礙眼, 可以叫它不要印: unset mouse
  5. 下面好像太擠了。 這是 margin 參數在管的。 先查一下: show margin 再改適當的設定值: set bmargin 5 再查看一次: show margin 最後重畫: replot

什麼, 要把所有 id 全部用手一個一個敲進去!? 機械化, 重複性的動作, 由人來做, 這樣對嗎? 請開另外一個命令列視窗, 在 bash 底下: (魔術表演, 暫時不理解沒有關係) perl -ne 'print qq("$1" ) , $.-1 , ",\\\n" if /\d+\s+(\w+)/' freq.txt > go.gpt 用編輯器進入 go.gpt, 在最上面加上一列 "set xtics (\" 並將最下面一列尾巴的 ",\" 改成 ")"。 可以猜得出來, 這裡每列最後面的 "\" 表示 "這個指令還沒打完, 但不得已要換列, 請暫時先不要處理"。 回到 gnuplot 視窗, 下 load "go.gpt" 最後 replot 大功告成。 (圖案看起來有點醜嗎? 上面故意漏了一點東西...請自己修改。)

最後要把圖存檔, 方便日後使用 (例如貼到文件裡面去)。

  1. 看一下目前的驅動程式: show term
  2. 看一下目前的輸出檔名: show output
  3. 改採 png 格式輸出: set term png
  4. 輸出到 freq.png 檔案裡面去: set output "freq.png"
  5. 重畫: replot 螢幕沒有任何變化; 但家裡多出一個 freq.png 檔。
  6. 用 file 檢查它的屬性; 用 ee 或 xli 或 xloadimage 看圖。
  7. 再將輸出切回螢幕, 以免以後畫圖都畫到檔案裡面去, 根本看不見: set term x11; set output (若是使用 windows 版, 請將 x11 改成 windows)。

上機次數統計表

統計登入次數及連線總時數

這一節, 我們的目的是要以每個人登入的次數及連線總分鐘數當做 x-y 座標, 畫出像這樣的圖:

上機次數-分鐘數統計表

首先計算每人連線總分鐘數: (魔術表演, 暫時不理解沒有關係) perl -ne '$f{$1}+=$2*60+$3 if /^(\w+).*\((\d\d):(\d\d)\)/ ; END { foreach (keys %f) { printf "$_:$f{$_}\n"; } }' lastlog.txt > time.txt

接下來在 gnuplot 當中試用一下 set label ... 指令:

set label "good" at 3,1
set label "ok" at 1,3
plot sin(x)
plot x*x
set xrange [0:5]
set yrange [0:5]
replot

可以看出: set label 單獨使用並沒有效果, 還是必須等 plot 指令執行後才會出現。 而且 gnuplot 自動調整繪圖範圍的依據, 是 plot 的函數, 而不是 label。 所以必須自己設定 xrange 與 yrange。

所以我們必須從 freq.txttime.txt 產生出 freq-time.gpt。 以下動作, 請自行分解, 把中間過程攔截下來觀察, 不要只管產生結果。 首先調整 freq.txt 兩欄的順序, 把 id 調到前面, 次數放到後面, 並且依 id 排序: perl -ne 'print "$2:$1\n" if /(\w+)\s+(\w+)/' freq.txt | sort > a 同樣地, 依 id 為 time.txt 排序: sort time.txt > b 最後把兩個檔案並排, 並且轉成 gnuplot 的指令: join -t : a b | perl -ne 'print qq(set label "$1" at $2,$3 center\n) if /(\w+):(\w+):(\w+)/' > freq-time.gpt 然後就可以進入 gnuplot, 開始畫圖:

        load "freq-time.gpt"
        plot 0
        set xrange [0:50]
        set yrange [0:2000]
        replot

Q: 如何迅速查出 y 的範圍? 一樣用 sort, 不過需要 -t 這個 option。

像這種大部分資料擠在左下角的圖, 如果改用 logscale 畫, 效果會比較好一點。 但要用 logscale, 資料裡面就不可以有 0 或負數。 perl -ne 'print unless /,0 /' freq-time.gpt > freq-time-nozero.gpt

        load "freq-time-nozero.gpt"
        set logscale
        set xrange [12:50]
        set yrange [300:2000]
        plot 0

提醒: 這篇講義的重點不是統計次數/時間, 而是 如何處理有規律的文字檔以產生統計圖表。 它的應用場合非常廣泛, 不限於此。



命令列讀本

  1. 目錄
  2. 前言
  3. 瀏覽
  4. 套件
  5. 圖片整形
  6. 老鼠迷宮
  7. 迷宮積木
  8. 誰常上機?
  9. 網頁做簡報
  10. tidy 網頁
  11. 編碼
  12. 客製
  13. Regexp
  14. 目錄比較
  15. 聯集差集

附錄

  1. GUI 求生
  2. 基本指令
  3. 阿貴管理