顯示具有 shell 標籤的文章。 顯示所有文章
顯示具有 shell 標籤的文章。 顯示所有文章

2016-08-15

Shell: 如何刪除無用的 Ubuntu modules ?

如何刪除無用的 Ubuntu modules ?

結論:

> dpkg -l linux-image-[0-9]*-generic | awk '/^ii/{ print $2}' | grep -v -e `uname -r` | sort -V | head -n -1 | xargs apt-get purge -y 
> dpkg -l linux-headers-[0-9]*-generic | awk '/^ii/{ print $2}' | grep -v -e `uname -r` | sort -V | head -n -1 | xargs apt-get purge -y 

廢話:

好意也時會帶來惡果.

正文:

當你安裝完 Ubuntu Server 後, 如果沒有關掉自動更新, ubuntu 會很貼心的將他每一次的 release 都放一份映象檔到你的 Server 上. 但可喜可賀的, 假如你從不需要關機(也沒有當機), 當然你用的還是你原先安裝的那個版本. 但不幸的事總是會伴隨好意來到. 雖然每個映象檔大約也不過 200M(目前, 誰都知道系統是會胖的), 但以每個月更新 1 到 2 次的頻率, 不到半年就佔去你 1G 的空間了, 因為自動更新雖然會幫你自動安裝, 但不會幫你自動清理用不著的檔案. 下載的檔案一般會放在 /lib/modules 目錄下.
> du -hd1 /lib/modules
du -hd1 /lib/modules
// 197M    /lib/modules/3.19.0-65-generic
// 197M    /lib/modules/3.19.0-64-generic
// 197M    /lib/modules/3.19.0-66-generic
// 197M    /lib/modules/3.19.0-61-generic
// 788M    /lib/modules
但更大的不幸是你非常有專業水準的為系統分割了磁區或限定了容量, 或本來你的硬碟就不怎麼大. 所以, 準備好你的杯子, 因為悲劇發生了.
> df -h
// Filesystem      Size  Used Avail Use% Mounted on
// /dev/sda1       9.9G  3.7G  5.7G  40% /
以三個月使用 1G 來計算, 悲劇將在一年後發生.
你可以選擇直接刪除 /lib/modules 下用不著的檔案. 但你最好還是先看看你目前使用的系統模組是哪個版本. 使用 uname -r. 記得到Ubuntu release 看一下你用的版本(version)跟核心(kernel)對照. 也可以順便看一下詳細有哪些分支.
>  uname -r
// 3.19.0-65-generic
以這個例子來說, 3.19.0-64-generic 和 3.19.0-61-generic 是舊的. 通常我們會保留前一個版本, 也不安裝最新的版本, 再加上目前使用的版本, 一共有三個版本.
雖然直接刪除 /lib/modules 下用不著的檔案不失為一個簡單的方法. 但還是有很多不需要的安裝檔在其他地方. 使用 apt-get remove是比較好的方式.
> sudo apt-get remove -y --purge 3.19.0-61-generic*
// or 
> sudo apt-get purge -y 3.19.0-61-generic*
// 簡化的參數
要移除前, 先找出目前安裝過的版本.
> dpkg --list | grep  linux-image-.*-generic | awk '{print $2}'
// or
> dpkg -l | grep  linux-image-.*-generic | awk '{print $2}'
linux-image-3.19.0-61-generic
linux-image-3.19.0-64-generic
linux-image-3.19.0-65-generic
linux-image-3.19.0-66-generic
linux-image-extra-3.19.0-61-generic
linux-image-extra-3.19.0-64-generic
linux-image-extra-3.19.0-65-generic
linux-image-extra-3.19.0-66-generic
再移除,
> apt-get purge -y 3.19.0-61-generic*
// 選擇要移除的
// 或
> dpkg -l linux-image-[0-9]*-generic | awk '/^ii/{ print $2}' | grep -v -e `uname -r` | sort -V | head -n -1 | xargs apt-get purge -y 
// 使用 grep -v -e `uname -r` 去掉"目前的版本"
// 使用 sort -V 依版本先後排序
// 使用 head -n -1 去掉"最後的版本"
如果不需要 “header”, 也可以移除
> dpkg -l linux-headers-[0-9]*-generic | awk '/^ii/{ print $2}' | grep -v -e `uname -r` | sort -V | head -n -1 | xargs apt-get purge -y 
要裝回來, 就用 apt-get install,
> apt-get install linux-image-3.19.0-61-generic
最後你可以將他寫成 script, 放到開機啟動或 crontab 去執行.

2016-07-24

Shell:如何在 ps 中不要看見自己?

如何在 ps 中不要看見自己?

結論:

> ps -ef | grep -v grep | grep 
或, 利用將 PATTERN 字串改為第一個字以 [] 框起來的正規表達式來過濾.
> ps -ef | grep [P]ATTERN

廢話:

自己從來是一件麻煩的事.

正文:

在 linux 文字模式上工作, 有個指令不會少打, 頻率大概只會少於 lsls 會列出目前目錄下的所有檔案跟目錄; ps 則會列出目前執行的程序.
這是因為有太多程式在背景中執行, 而他們也不會因為你點了滑鼠就跳出來告訴你, “我在這裡!”. 他們總是默默工作, 直到你點名才會知道他們存在. 而用來點名的指令就是ps. 使用 ps 只是點了你自己執行的程序名, 想知道目前有哪些程序在這台電腦上執行你必須下 ps -ef 或ps aux.
通常點名, 我們也希望有效率一點, 所以我們會用 grep 來協助抽點. 例如, 想知道目前有沒有 node 程式在執行, 可以下 ps -ef | grep node. 但這樣的結果有時候不夠完美, 因為你剛剛下的指令也是程序之一, 其中也有"node"字串, grep 也會將他挑選出來, 而這通常不是我們要的結果.
還好, grep 提供 -v 這個參數, 他會將挑出不符合的結果. 利用這個, 將 ps -ef | grep node 的結果再過濾一遍, 使用 ps -ef | grep node | grep -v grep 就能得到我們真正希望的結果.
不過, 有些 shell 會幫你在 grep 中加上 --color=auto 來為傳回結果著色. 為避免失去這個效果, 可以將真的要過濾條件放在最後面. 在這個例子, 也就是 ps -ef | grep -v grep | grep node.
最後我們得到 ps -ef | grep -v grep | grep  這樣的命令模式.
在這樣的使用案例上, 透過一個小技巧利用 regexp 的邊際效應, 可以改寫, 如下
> ps -ef | grep [n]ode
這是因為 字串 “ps -ef | grep node” 是吻合 /node/ . 而 字串 “ps -ef | grep [n]ode” 並不吻合 /[n]ode//[n]ode/ 要求一個 “n” 後面接 “ode”, 而 grep 的字串模板是"n"後面接 “]ode”.
或許, 你認為這樣太麻煩, 用 眼睛 看, 不就能區分出來了? 但實際上, 這常常被應用在 shell script 中, 來去判斷某些程序是否在執行了.

Shell&JS: 簡單的集合運算

簡單的集合運算

結論:

// A 與 B 目錄下的檔案
> ls A B
// A:
// apple  book  can  cup  chair
// 
// B:
// book  cup  dog  hat  table
//

// 交集: A&B
> (ls A;ls B) | sort | uniq -d 
// book
// cup

// 聯集: A|B
> (ls A;ls B) | sort | uniq 
// apple
// book
// can
// cup
// chair
// dog
// hat
// table

//差集: A-B , 注意 `ls B` 重複兩次, 不是筆誤.
> (ls B;ls B;ls A) | sort | uniq -u
// apple
// can
// chair

//對稱差: A^B = (A-B)|(B-A), A^B = (A|B)-(A&B)
> (ls A;ls B) | sort | uniq -u
// apple
// can
// chair
// dog
// hat
// table

廢話:

集合在數學中占有很大的分量, 但基礎是簡單的. 在程式中也經常需要用到. 使用程式語言時, 只要使用 for 跟 if, 應該就能簡單的達成交集, 聯集和差集的運算. 但有時候特地去寫程式之類, 還是算了吧.
在 shell 中利用 sort 跟 uniq 也能達成簡單的集合運算. 以下分別舉例來說明如何達成集合最基本的運算.

正文:

  • 交集: 兩個集合中相同的元素. 使用 JS 陣列時, 可以如下處理,
var A = ['apple', 'book', 'can', 'cup', 'chair'];
var B = ['book', 'cup', 'dog', 'hat', 'table'];
var S = [];

for (var i = 0; i < A.length; i++){
    var p = A[i];
    for (var j = 0; j < B.length; j++){
        var q = B[j];
        if (p == q) {           
            S.push(q);
            break;
        }
    }
}
console.log(S);
// [ 'book', 'cup' ]
同樣的假設 A 和 B 目錄下各有數個檔案. 如下, A 有 ‘apple’, ‘book’, ‘can’, ‘cup’, ‘chair’ B 有 ‘book’, ‘cup’, ‘dog’, ‘hat’, ‘table’ 使用 shell 指令來處理,
> (ls A;ls B) | sort | uniq -d
// book
// cup
使用 sort 是因為 uniq 的輸入必須是已經被排序的. 如果給 uniq 一個不是排序的資料, 將會得到錯誤的結果. 且 uniq -d 是列出重複的資料. 整個命令的意思是說: 列出A目錄跟B目錄下的檔案, 把他們放在一起, 丟給sort排序, 然後再列出重複的資料.
利用同樣的思路, 改用 JS 來處理, 如下
var A = ['apple', 'book', 'can', 'cup', 'chair'];
var B = ['book', 'cup', 'dog', 'hat', 'table'];
var X = A.concat(B);
var S = {};

for (var i=0; i< X.length; i++){
    S[X[i]] = (S[X[i]] || 0) + 1;    
}
// 上面的 JS 以計算重複次數代替判斷重複, 需要遍歷整個陣列 X,
// 所以有無排序不影響結果.

for (obj in S){
    if (S[obj] > 1) {
        console.log(obj);
    }
}
// book
// cup
  • 聯集: 有出現在兩個集合中元素.
var X = A.concat(B);
var S = {};

X.forEach(function(obj){
    S[obj] = (S[obj] || 0) + 1;
});
// 改用 Array 的 `forEach` 方法來計算重複次數

for (obj in S){
    console.log(obj);    
}
// 因在計算重複次數時, 已將相同的元素合併計算次數, 
// 所以只要將 S 的所有元素印出, 即可.
// apple
// book
// can
// cup
// chair
// dog
// hat
// table
而使用 shell 指令來處理, 就更簡單, 只要下 (ls A;ls B) | sort | uniq 就行了.
  • 差集: 有出現在其中一個集合並不出現在另一個集合的元素. 下面是可用的 JS 程式碼,
var S = [];

for (var i = 0; i < A.length; i++){
    var p = A[i];
    S.push(p);
    for (var j = 0; j < B.length; j++){
        var q = B[j];
        if (p == q) {                       
            S.pop();
            break;
        }
    }
}
console.log(S);
// [ 'apple', 'can', 'chair' ]
實作上程式的邏輯有許多方式, 這裡採用先將 A 陣列的元素放入一個容器後, 再用迴圈判斷是否存在 B 陣列中, 如果存在就再將他移出的. 好處是可以省去使用判斷是否存在 B 陣列旗標.
在 Shell 中, 要達成差集必須要有一點巧思. 我們知道差集是 A 集合減去 B 集合剩下來的元素, 所以該差集 不會 包含 B 集合的元素. 而利用uniq -u 可以列出不重複的資料, 故 (ls B;ls B) | sort | uniq -u 應該不會傳回任何資料, 因為 B 目錄下的檔案全部被重複了兩遍. 同理, 當使用(ls B;ls B;ls A) | sort | uniq -u 時, 傳回的就會是 A 目錄中不與 B 重複的檔案. 因為 A 跟 B 重複的元素, 必定也是 B 的元素, 而已經重複兩次 B 元素再加 A 重複 B 的元素還是重複, 都會被 uniq -u 消除掉. 所以在這裡 ls B 要重複兩次. 如果只是單純的(ls B;ls A) | sort | uniq -u 則會列出 A 和 B 目錄下的不重複的檔案. 也就是 A 目錄下的檔案加上 B 目錄的下的檔案再減去 A 和 B 目錄下都有的檔案, 也就是 A 跟 B 的對稱差.
根據這個思路, 改寫 JS 如下,
var X = A.concat(B).concat(B);
var S = {};

X.forEach(function(obj){
    S[obj] = (S[obj] || 0) + 1;
});

for (obj in S){
    if (S[obj]==1) {
        console.log(obj);    
    }
}
// 印出只出現一次的元素
// apple
// can
// chair
其實, 集合的運算是可以類比如位元的運算. 重新整理上面的案例, 將資料整理, 賦予每一個元素一個數值, 如下,
// apple=1, book=2, can=4, cup=8, chair=16, dog=32, hat=64, table=128;
// A = ['apple', 'book', 'can', 'cup', 'chair'] => [1, 2, 4, 8, 16]
// B = ['book', 'cup', 'dog', 'hat', 'table'] => [2, 8, 32, 64, 128]

//將 A, B 改用數值表示
  var A = 1 + 2 + 4 + 8 + 16;
// A, 十進位:  31,  二進位: 1 1111

  var B = 2 + 8 + 32+ 64+ 128;
// B, 十進位: 234,  二進位: 1110 1010

// 交集: A&B
  var x = A & B;
// 10  
// 十進位:  10,  二進位: 1010
// 10 => 2 + 8 => book, cup

// 聯集: A|B
  var x = A | B;
// 255
// 十進位:  255,  二進位: 1111 1111
// 255 => 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 
// => apple, book, can, cup, chair, dog, hat, table

// 差集: A-B?
  var x = (A|B) - B;  
// 21
// 十進位:  21,  二進位: 0001 0101
// 21 => 1 + 4 + 16 => apple, can, chair
// A集合去掉B集合的元素可表示為 A 與 B 的聯集(A|B)再去掉 B 集合, 
// 這樣就只會剩下沒有 B 集合元素的 A 集合元素.
// 這裡沒辦法直接 `A-B` 是因為若有一 B 集合元素不在 A 集合中, 
// 則會變成空集合減扣掉某一元素, 在這邊就沒辦法正確表示.

// 對稱差: A ^ B
  var x = A ^ B;
// 245 
// 十進位:  245,  二進位: 1111 0101 
// 245 = 1 + 4 + 16 + 32 + 64 + 128 =>apple, can, chair, dog, hat, table
不過 JS 的位元運算並不像 C 語言一樣有較高的效率. 雖說在運算上簡單不需要迴圈, 但要將結果轉回對映的元素, 仍要進行一串二進位運算. 總的來說, 程式碼也不算特別簡潔. 不過, 在一些特殊的運用場合仍有其用途.
所以集合運算可對應位元運算如下,
  • 交集: A&B, and 運算.
  • 聯集: A|B, or 運算.
  • 差集: (A|B)-B, 特殊條件下的減法運算.
  • 對稱差: A^B, xor 運算.

2016-07-11

Sublime: 如何使用 ssh tunnels 編輯遠端檔案

如何在 sublime text 使用 ssh tunnels 編輯遠端檔案?

結論:

  1. 透過 putty 建立一個 ssh tunnels 連線. 在 Putty Configuration 中, 增加下列設定
Connection -> SSH -> Tunnels
Source Port: 52698
Destination: localhost:52698
[ ]local [V] Remote [ ]Dynamic
設定完後, 再按[Add], 便可頁面上的 list Box 增加一個 R52698 localhost:52698 的項目. 這約等同直接使用下面的指令,
> ssh -R 52698:localhost:52698 user@example.com
其中 user@example.com 的 user 要改成你在遠端主機上的帳號, example.com 要改成遠端主機的 IP 或名稱.
  1. 在 remote 端, 安裝一個 shell script 來作為 sublime text 的遠端服務.
> wget -O /usr/local/bin/rsub https://raw.github.com/aurora/rmate/master/rmate
// 下載支援 rmate 的 shell script到特定路徑, 並檔名存成 rsub
> chmod +x /usr/local/bin/rsub
// 將 rsub 設為可執行
> rsub -h
usage: rsub [arguments] file-path
// 測試是否可正確執行 rsub
https://github.com/aurora/rmate 是 TextMate rmate 的 shell script 版本. 其他版本可參見, https://github.com/textmate/rmate
  1. 安裝 sublime text 版的 rmate
[Ctrl] + [Shift] + [p]
// 開啟 Package Control
Install Package 
// 選擇安裝
rsub
// 選擇套件 rsub 
  1. 開始遠端編輯 本機上保持開啟 sublime text, 並回到遠端的 console. 執行下列指令.
> rsub ~/.profile
這會使本機上的 sublime text 自動建立一個本地端的副本檔案, 並開啟該檔案. 而當在 sublime text 中存檔時, 會同時更新遠端上的檔案.

廢話:

不管黑客(hacker)還是怪客(cracker) 多熟練 Vi 能抓滑鼠的就是宅客(homeworker).
如果你的工作記憶只能存下一頁 美女圖 程式, 而你卻有上千行程式碼時, 在 GUI 底下讓滑鼠來支援捲動還是比較方便的. 又如果你這樣想, 而你也是選用 sublime text 當你在視窗環境的文字編輯器, 這時, 你就用的著上面的技巧了.
補充說明:
  • TextMate: 在 Mac 下常用文字編輯器.
  • rmate: TextMate 以 ruby 實作, 可透過 ssh session 編輯遠端檔案.

正文:

同結論.

2016-07-05

Linux: 如何找到有特定內容的檔案名稱?

如何找到有特定內容的檔案名稱?

結論:

> find ./ -type f | xargs -i grep -l 'target' {}

廢話:

大海撈針, 乾草堆裡找線頭. 絕對不要捲起袖子自己做.
現在還有人沒使用過 Google 的搜尋服務? 曾幾何時 不用說就是1998, Google誕生之前. 可憐的蓋世(GAIS)搜索, 還有人記得?, 各家網頁黃頁服務爭鳴, Yahoo 也因此崛起. 但如今還有幾人知曉蕃薯藤, 更不用說是其他小型入口網站. 而原本占據在瀏覽器 書籤 或 我的最愛 的各種網頁索引或目錄網站也早已消失不見. 取而代之的是 搜尋.

正文:

茫茫資訊海, 誰來搜尋? 一曰 find, 再者 grep.
find 功力強大, 能幫助你找到檔案, 但並不能幫助你找到檔案內有甚麼? 簡單的介紹用法, 如下.
根據檔名來找,
> find . -name "*.log" -print
//find  -name  -print
//find 後面接要搜尋的路徑, -name 後面接要比對的模式, 用 -print 將找到的檔案印出
接在 find 命令後的表達式, 主要可以區分 測試(Tests), 行動(Actions), 關聯操作(Operators) 等部分.
  • 測試(Tests)會根據參數或描述傳回 true 或 false.
  • 行動(Actions)指示找到檔案後要做的事, 例如:-print`指示將找到的路徑列印出來.
  • 關聯操作(Operators)則是用來連接不同的參數或描述.-o 意指 OR, -a 意指 AND. 沒指定就是-a.
根據名稱來找,
> find . -name "*.log" -print
上述範例是要"印出所有當前目錄下, 名字以 .log 結尾的".
根據類型來找, find -type
> find . -type f -name "*.log" -print
上述範例是要"印出所有當前路徑下, 類型是"檔案", 名字以 .log 結尾的. -type 常用的參數, 如下,
`d`    目錄
`p`    具名的 pipe
`f`    一般檔案
`l`    連結檔
`s`    socket
排除特定路徑,
> find . -path "./test" -prune -o -print
//印出所有當前目錄下, 除了在路徑./test 下的檔案.
這是因為 -prune 條件成立時, 總是傳回 true, 且條件是目錄時不會往下處理, 故可以利用 -o 來造成判斷的捷徑.
舉上例來說, 當比對到 “./test” 時, 傳回 true. 而後面是邏輯 OR 運算, 不管後面是 true 或 false , 結果都會是 true, 所以就不用再判斷, 也不用做下去. 因此, -o之後的 -print 不會被執行到. 但當比對的不吻合 “./test”, 傳回 false, 而可繼續執行其後的條件判斷.
如果不使用 -prune, 則只有路徑完全一樣的, 才會被排除掉.
> find . -path "./test" -o -print
//印出所有當前目錄下, 除了路徑是 "./test", 
//所以如果檔案路徑是"./test/a.txt" 則會被印出, 
//但路徑 "./test" 不會被印出.
所以, 以下與使用 -prune 約略等效,
> find . -path "./test/*" -o -print
//印出所有當前目錄下, 除了路徑吻合 "./test/" 檔案(注意, 最後有斜線).
//但 "./test"(注意, 最後沒有斜線) 本身會印出來
依檔案的時間來找, 可用的參數, 如下
-mtime n    檔案的最後修改時間, 單位是天.
-mmin  n    檔案的最後修改時間, 單位是分鐘.
-atime n    檔案的最後存取時間, 單位是天.
-amin  n    檔案的最後存取時間, 單位是分鐘
-ctime n    檔案狀態相關資訊最後修改的時間, 單位是天.
-cmin  n    檔案狀態相關資訊最後修改的時間, 單位是分鐘.
簡單來說, 後面接"time"的單位是天, 接"min"的是分鐘. 時間都是從現在開始往前(過去)推的. 然後, 從推定的時間點, 之前就用+, 之後用 -. 例如, 如果現在是 2016-07-06 12:00:00 則 -mtime +1 就是說距現在 1天的時間點, 也就是 2016-07-05 12:00:00 之前被修改過的檔案.而 -mtime -1 是指 2016-07-05 12:00:00 之後到現在被修改過的檔案. 簡言之, -mtime +1 可以翻譯成 1 天被修改過的檔案. -mtime -1 可以翻譯成 1 天被修改過的檔案.
前面接"m"的是指被修改過. 接"a"是指被存取過(一般而言有被修改當然就有被存取, 但也有不先讀取資料就直接改變檔案內容的方法). 接"c"是指雖然沒被存取, 但有改變權限, 群組, 所有人或修改時間之類的(修改算狀態變動, 但存取不算).
依檔案的大小來找,
> find . -size 30M -print
//印出所有當前目錄下, 檔案大小等於 30M 的檔案.
// -size n[cwbkMG]
`b`    blocks 一般為 512-byte 
`c`    bytes
`w`    1 個 word 等於 2 個 bytes
`k`    Kilobytes (等於 1024 bytes)
`M`    Megabytes (等於 1048576 bytes)
`G`    for Gigabytes (等於 1073741824 bytes)
+ 是大於, -是小於, 例如: -size +30M 檔案大於 30M 的檔案.
找檔案用 find, 找內容用 grep. 接下來介紹 grep大誤 要會熟用 grep 要學會正規表達式(regex), 但沒打算在這一併介紹. 目前只要知道 grep 命令的參數格式, Usage: grep [OPTION]... PATTERN [FILE]...
簡單說明, grep -l 能讓你只印出檔名, 而不是內容比對吻合的文字. xargs -i 來幫你填入參數, 用{}指定位置.
利用 find 過濾要找的檔案範圍, 利用管道 | 將找到的檔名交給 grep 分析內容. 然後印出有吻合 grep 指令所給定 PATTERN 內容的檔名.
> find ./ -type f | xargs -i grep -l 'target' {}
// 利用 `xargs` 把 `find` 找到的檔名填入"{}"處
現在, 你也可以自製一個桌面板的 Google 搜尋了. :P