組合的力量


本單元主要參考 GNU textutils 軟體的手冊寫成, 特別推薦 Opening the software toolbox "Toolbox Introduction" 與 "Putting the tools together" 兩節的觀念。

學會 以組合的方式操作電腦, 才能夠以有限的記憶力解決成千上萬沒有見過的問題. 「技術人員觀點」一文當中的 「組合的力量」 一段, 以類比的方式說明這種思考模式的重要性; 而這篇技術性的講義則提供具體的操作實例。

  1. 請複習 輸入輸出重新導向命令結果代換 兩大重要觀念, 以及 cat、 head、 tail、 wc、 grep、 sed 等命令。
  2. sort 命令: 以列為單位, 對輸入資料排序.
    1. -n 把資料視為數字排序
    2. -r 倒過來排
    3. +3 -5 根據第 3,4 兩欄排序
    4. -t ':' 把 ':' 視為欄位的分隔符號
  3. cut 命令: 只保留某些欄位印出 (和 grep 正好 "垂直")
    1. -c 11-20,31-40 印出每列的第 11 到第 20 個字元, 以及第 31 到第 40 個字元.
    2. -f 2-4,5-8 印出每列的第 2,3,4,5,6,7,8 等欄位 (內定以 tab 鍵分隔欄位)
    3. -d ':' 把 : 當做欄位的分隔字元
  4. uniq 命令: 假設輸入資料已經過排序, uniq 可以用來把重複出現的列刪除到只出現一次.
    1. -c 額外印出每列重複了多少次
    2. -u 只列出沒有重複的列
    3. -d 只列出有重複的列
  5. tr 命令: 把輸入資料裡面的每個 ... 字元都改成 ... 範例:
    1. 把 DOS/Windows 傳上來的檔案後面的 ^M 去掉 (把結果印在螢幕上)
      tr -d '\r' < config.sys
    2. 把所有英文字母變成小寫 (把結果印在螢幕上)
      tr 'A-Z' 'a-z' < config.sys
  6. expand 命令: 把所有的 tab 鍵都換成適當個數的空白鍵
  7. paste 命令: 把檔案並排印出 (和 cat 正好 "垂直")
  8. join 命令: 類似 paste, 但是並排時未必把來自不同檔案的同一列合成一列, 而是依據特定欄位內相同的值來決定那幾列要合併. 輸入資料必須事先排序過. 把 join 命令與其他命令組合運用, 可以用純文字檔案來實作簡單的資料庫系統.

更多範例:

  1. 看看目前目錄下那些檔案占用的空間最多
    ls -l | sort -n +4
  2. 看看整個系統內那些程序吃記憶體吃得最兇
    ps aux | sort -n +3
  3. 看看那些使用者的 "GECOS" 欄位有資訊 (見 man 5 passwd)
    grep ',' /etc/passwd | cut -d ':' -f 5
  4. 看看自己家裡面所有最上層的子目錄各用了多少空間
    du -s $(ls -l ~ | grep '^d' | cut -c 55-)
  5. 把使用者 ckhung 所有正在執行的程序全部中斷掉
    kill $(ps aux | grep '^ckhung' | cut -c 9-16)
  6. 看看有那些使用者有程序在執行 (無論有無控制臺)
    ps aux | cut -c 1-8 | sort | uniq
    上例中如果用 uniq -c 則列出每個人正在執行多少個程序
  7. (以下只是一個技術範例; 但實際上不論從禮貌/隱私/安全 的角度來看, 這麼做都不太好, 請不要濫用!) 假設過去幾個月以來我所收到的所有信件都放在 mbox 檔當中; 而我有一封公開信放在 announce 檔案內. 如何把公開信寄給 "過去幾個月以來曾經寄信給我的人"? 有一些信的寄件人不需要收到我的公開信, 像是系統 (退信), 我自己, 所有的英國筆友... 等等人. 我把他們的 e-mail 樣版都放在 exclude 這個檔案內, 內容像這樣:
            MAILER-DAEMON
            ckhung
            \.uk$
    
    深呼吸...請看: mail $(perl -ne 'print "$1\n" if /^From:.*<(.*@.*)>/' mbox | sort | uniq | grep -v -f exclude a) < announce (通通打在同一列, 不要換列!)
  8. 想找找看 time_t 這個型別究竟是在那個系統標頭檔中定義的: grep time_t $(find /usr/include/ -type f) | grep typedef
  9. 製作 orig 這個目錄下所有檔案的 md5 checksum: md5sum $(find orig -type f) > orig.md5 用途: 想比較兩個很相似的目錄, 是否它們底下所有相對應的檔案內容都相同? 可以對這兩個目錄下上述的命令, 再用 diff 比較結果 (orig.md5 與 new.md5). 如果結果相同, 那麼這兩個目錄的內容完全相同的機率幾乎是 1.
  10. 看看目前目錄 (及子目錄孫目錄 ...) 下有那些檔案是 perl 程式, 並依大小排列出來: file $(find . -type f) | grep 'perl commands text' | sed 's/:.*$//' | xargs wc | sort -n
  11. 把自己家裡所有已公開的網頁包成一個壓縮檔: tar czf mypages.tgz $(find public_html/ -type f -perm +044)
  12. 在 abc 目錄下 (含子目錄, 孫目錄, ...) 尋找所有內含 pqr 字串的檔案, 並把這些 pqr 都改成 xyz: grep -l pqr $(find abc -type f) > ttt; perl -i -pe 's/pqr/xyz' $(cat ttt)
  13. 把目前目錄下所有最近三天內修改過的檔案按照大小排出來: ls -l $(find . -type f -mtime -3) | sort -n +4
  14. 因應 CLE 首頁網址由 http://cle.linux.org.tw/CLE/ 改為 http://cle.linux.org.tw/ 想一次將自己的 ~/public_html/ 目錄下所有提及 CLE 舊網址的地方一口氣更新: 先找出所有提及 CLE 舊網址的檔案: grep -l 'http://cle.linux.org.tw/CLE' $(find . -type f) > ~/f 再用 regexp 一次更新: perl -pi.bak -e 's#http://cle.linux.org.tw/CLE/?#http://cle.linux.org.tw/#g' $(cat ~/f)
  15. 把目前目錄底下所有 dos 格式文字檔, 轉成 unix 格式: file $(find . -type ) | grep -i text | sed 's/: .*//' > /tmp/listing; perl -i -pe 's/\015//' $(cat /tmp/listing)
  16. 本來有很多 .mp3 檔分別放在很多子目錄底下。 但是有些較舊的 mp3 player 處理目錄會出問題, 所以將它們全部移到目前這一層目錄。 現在想將這些已經清空的目錄刪除掉: rmdir $(ls | grep -v '\.mp3$' )
  17. env | grep -i tw 出現許多與 locale 相關的環境變數; 現在想將所有變數一次清除: unset $(env | grep -i tw | sed 's/=.*//')
  18. all.txt 檔案裡面列有許多檔名。 希望將這當中所有的圖形檔搬到 image/ 目錄下: mv $(file $(cat all.txt) | grep -i image | sed 's/:.*//') png
  19. 在 ubuntu 底下, 想查看所有 wiki 軟體的說明: apt-cache show $(apt-cache search wiki | grep -i wiki | grep -v '^lib' | sed 's/ - .*//') > wiki.txt
  20. 下了許多 wget -nv ... 指令, 到一個慢速網站抓檔。 不小心把 screen 關掉了。 把尚未抓完的檔名放到 a, 把 history 放到 b, 然後 for f in $(cat ~/a) ; do grep $f ~/.history ; done > ~/c

類似文章

  1. Hone Your Scripting With a Regexp Toolbox