2009年1月31日 星期六
2009年1月11日 星期日
2009年1月9日 星期五
[Linux] 透過指令直接設定 IP、Netmask、Gateway
那........怎麼辦呢? 我要改 IP 跟 Gateway 阿!
找了一下資料,發現可以透過指令方式直接指定:(設定範例)
root> ifconfig eth0 192.168.1.1 netmask 255.255.255.0
root> route add default gw 192.168.1.254
這樣就成功啦!
參考資料
2008年12月8日 星期一
功能強悍的防火牆 ZeroShell
之前使用 Untangle,發現實在是一套不錯的軟體,功能相當強大,已經達到 UTM 可以作到的程度了!
但是唯一的缺憾就是還不能對 Layer 7 application 進行 Qos 的管理,也許未來會推出相關套件也不一定…
因此就在網路上另外找到這套「ZeroShell」,他不大,可至官方網站下載 Live CD 來使用,也可以安裝至硬碟上(IDE only,如果主機板支援將 SATA 轉為 Legacy mode 應該也是可以)
而 ZeroShell 有哪些功能呢? NAT、DHCP、DNS、VPN、L7-Filter、Qos、RADIUS、LDAP、Load Balance、X.509 …等等一狗票的功能,詳細的功能可以到官方網站去看。
只是他恰巧符合我需要的 L7-Filter + QoS 的功能,因此目前選中他作為測試!
而 ZeroShell 中所提供的所有功能,幾乎都是從 open source 的專案來的,例如:OpenVPN、OpenLDAP、l7-filter …等等,因此要找到支援與協助應該不是太困難。
以下列出最近測試時,發現到要注意的設定重點:
設定資訊的儲存
上面有提到 ZeroShell 是以 Live CD 的方式啟動的,因此每次重開機設定都會不見;查了一下官方網站,有說到只要設定完成後,進入「SYSTEM –> Setup –> Profiles」將 profile 備份即可,進入時他會偵測電腦上的硬碟與分割,讓管理者可以自行決定 Profile 要放哪。
但真的有那麼美好嗎? 我實際測試是沒有……只有對外網卡的設定會保留下來,其他都還是跑掉,這讓我很困擾,因此最後我還是把他裝到硬碟上去了…(裝到硬碟上後,也不用儲存 profile 了,因為設定不會跑掉)
NAT
原本以為設定好每個 NIC 的 IP address 跟 default gateway,自動產生 routing table 之後,NAT 也會跟著起來,但實際上並沒有……
但設定並不困難,只要進到「NETWORK –> Router –> NAT」,把對外的那一張網卡加到「NAT Enabled Interfaces」即可。
憑證(certificate)的啟用
若要使用憑證相關的服務(例如:OpenVPN),就需要將憑證啟用,啟用要從「SECURITY –> X.509 CA」中選擇要啟用的憑證,將其啟用。
而啟用的方式比較特別,需要先 revoke 之後再 renew,才能將憑證正式啟用。
OpenVPN
在 server 的部份,只要進入「NETWORK –> VPN」將這個功能啟動即可,而認證方式有三種(密碼、密碼 + 憑證、憑證)可以自由選擇,而若要加入密碼的認證,則必須指定外部的 RADIUS 或是 LDAP server 進行認證。
而 client 的部份,需要從官方網站下載連線設定檔,並修改裡面的設定(OpenVPN server 位址、憑證位置、金鑰位置)。
另外 CA 憑證的部份可以從 ZeroShell 的登入頁下載。
而 client 的憑證則可以到「USERS –> Users –> X509」中將金鑰資訊 export 出來。
QoS
設定 Qos 必須注意,Qos 僅能管理從特定網路介面出去的流量,無法管制進來的流量;雖然如此,透過針對不同網卡的管控,還是可以作到上下傳的頻寬控管。
另外載設定的部份就稍微複雜一點,就跟直接設定防火牆差不多,需要以下三個步驟:(下面的設定都在「NETWORK –> QoS 中」)
- 設定 QoS Class
在「Class Manager」中,可設定傳輸的優先權(Priority),最大傳輸頻寬(Max)以及保證頻寬(Guaranteed)。
- 將 Qos Class 與網路介面進行繫結
不一定設定的規則都要套用在每一個網路介面上,因此可以到「Interface Manager」中指定要將 QoS Class 套用到那一個網路介面。
- 設定規則,並指定其屬於哪個 Qos Class
設定了頻寬的限制、所要管理的網路介面後,就要設定規則與其匹配了,而規則設定需要到「Classifier」中進行設定,而 ZeroShell 提供的設定選項很多,幾乎 iptables 支援的功能他都做到 web 介面上了,當然也包含了 Layer 7 的選項囉!
L7 Filter 的更新
目前 ZeroShell 還沒有提供線上更新 Layer 7 pattern 的功能,若要更新的話,可以從 l7filter 的官方網站下載最新的 protocol definition,並將檔案命名為「l7-protocols.tgz」後,放到「/var/register/system/net/L7」資料夾內,重新開機即可。
2008年11月25日 星期二
Linux iptables Packet Flow Diagram
在鳥哥的網站上看到的:
另外在國外網站上偶然看到的,覺得蠻一目了然的,因此留下來
(資料來源:Quick HOWTO : Ch14 : Linux Firewalls Using iptables - Linux Home Networking)
另外還有一張更清楚的……不過很大就是了!
2008年11月21日 星期五
關閉 Ubuntu 惱人的系統嗶聲
如果安裝 Ubnutu 僅安裝 console mode 時,在敲鍵盤就會不時有討厭的嗶聲出現
在 GUI 裡面還可以在系統裡面關閉,但僅有 console 要怎麼關呢??
若要暫時先關閉,可以輸入以下指令:
shell> sudo rmmod pcspkr
如果要一勞永逸,就編輯「/etc/modprobe.d/blacklist」,並加入以下這一行:
blacklist pcspkr
這樣就可以永遠跟討厭的嗶嗶嗶說 goodbye 啦!
參考資料
2008年2月26日 星期二
Beginning Linux Programming 學習筆記(3) - Shell Programming (Shell Syntax)
在 Linux 中,shell 的功能是相當強大的,許多系統管理的功能都可以由 shell 來完成,跟其他程式語言相同,shell 也有其特定語法格式,分成以下幾個部分來說明。
Variables
變數定義的部分,必須注意到引號(單雙引號)的使用與影響,以下用範例來說明:
#!/bin/sh
myvar="Hi there"
echo $myvar
echo "$myvar"
echo '$myvar' #使用單引號就不會印出變數內容
echo $myvar #escape
echo Enter some text
read myvar
echo '$myvar' now equals $myvar
exit 0
# ========== 執行結果 ==========
# Hi there
# Hi there
# $myvar
# $myvar
# Enter some text
# Hello World! <== 這個部分是使用者輸入的
# $myvar now equals Hello World!
接著說明 Environment Variables,這個部分大多數是使用者個人的設定,以下列出幾個常用的環境變數:
Environment Variables | 描述 | 範例 |
$HOME | 使用者家目錄 | /home/godleon |
$PATH | PATH 設定(由冒號所隔開) | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games |
$PS1 | 命令提示字元 | ${debian_chroot:+($debian_chroot)}\u@\h:\w\$ |
$PS2 | 命令提示字元 | > |
$IFS | 用來隔開輸入參數的字元,通常為 space、tab 或 newline | |
$0 | shell 的名稱 | -bash |
$# | 傳入參數的數量 | 2 |
$$ | shell script 所屬的 process ID | 5465 |
繼續是 Parameter Variables,其用途很明顯,當然是跟傳入的參數有關:
Parameter Variables | 描述 | 範例 |
$1, $2, ...... | 傳入的參數 | para1, para2, ..... |
$* | 所有傳入的參數,以 $IFS 隔開 | para1 para2 para3 (假設 $IFS 為一個 space) |
$@ | 所有傳入的參數,不過並非由 $IFS 隔開,而是用一個 space 作間隔 | para1 para2 para3 |
以下用一個範例說明 environment/parameter variables 的使用:
#/bin/bash
salutation="Hello"
echo $salutation
echo "The program $0 is now running" # $0 (shell script 名稱)
echo "The second parameter was $2" # $2 (第二個參數)
echo "The first parameter was $1" # $1 (第一個參數)
echo "The parameter list was $*" # 參數列表
echo "The user's home directory is $HOME" # 家目錄
echo "Please enter a new greeting"
read salutation # 讀取使用者的輸入
echo $salutation
echo "The script is now complete"
exit 0
# ========== 執行結果 ==========
# Hello
# The program try_var.sh is now running
# The second parameter was two
# The first parameter was one
# The parameter list was one two
# The user home directory is /home/godleon
# Please enter a new greeting
# Hello Bash Shell
# Hello Bash Shell
# The script is now complete
Conditions
在 shell script 中可以用 test 指令來測試命令執行後所產生的 exit code,因此在每一個 shell script 的最後一行,建議都加上 exit code 來回應程式執行的結果。
在 shell 中,目前有三種 condition type,分別列表如下:
String Comparison | 結果 |
str1 = str2 | True,若 str1 等於 str2 |
str1 != str2 | True,若 str1 不等於 str2 |
-n str | True,若 str 不為 null |
-z str | True,若 string 為 null 或空白字串 |
Arithmetic Comparison | 結果 |
exp1 -eq exp2 | True,若兩個結果相同 |
exp1 -ne exp2 | True,若兩個結果不相同 |
exp1 -gt exp2 | True,若 exp1 大於 exp2 |
exp1 -ge exp2 | True,若 exp1 大於等於 exp2 |
exp1 -lt exp2 | True,若 exp1 小於 exp2 |
exp1 -le exp2 | True,若 exp 小於等於 exp2 |
! exp | True,若 exp 為 false |
File Conditional | 結果 |
-d file | True,若檔案為目錄 |
-e file | True,若檔案存在 |
-f file | True,若檔案為一般檔案 |
-g file | True,若檔案有設定 set-gid |
-r file | True,若檔案可讀取 |
-s file | True,或檔案大小不為 0 |
-u file | True,若檔案有設定 set-uid |
-w file | True,若檔案可寫入 |
-x file | True,若檔案可執行 |
以下用一個簡單範例來說明使用方式:
#!/bin/bash
if [ -f /bin/bash ]
then
echo "file /bin/bash exists"
fi
if [ -d /bin/bash ]
then
echo "/bin/bash is a directory"
else
echo "/bin/bash is not a directory"
fi
# ========== 執行結果 ==========
# file /bin/bash exists
# /bin/bash is not a directory
Control Structures
shell 提供了許多不同的控制結構,以下分別一一介紹:
IF
#!/bin/bash
echo "Is it morning? Please answer yes or no"
read timeofday
if [ $timeofday = "yes" ]
then
echo "Good morning"
elif [ $timeofday = "no" ]; then # 請注意此處,必須加上分號以及另外一個 then 喔!
echo "Good afternoon"
else
echo "sorry, $timeofday not recongnized. Enter yes or no"
exit 1
fi
exit 0
FOR
這個部分需要注意一下,變數必須以 space 為間隔!
#!/bin/bash
# 會將 $(ls *sh) 的結果,以 space 為間隔印出
for file in $(ls *.sh);
do
echo $file
done
exit 0
WHILE
#!/bin/bash
echo "Enter password"
read trythis
while [ $trythis != "secret" ];
do
echo "sorry, try again"
read trythis
done
exit 0
UNTIL
#!/bin/bash
# 無使用者登入
until who | grep $1 > /dev/null
do
sleep 60
done
# 偵測到使用者登入
echo "$1 has just logged in"
exit 0
CASE
#!/bin/bash
echo "Is it morning? Please answer yes or no"
read timeofday
case "$timeofday" in
yes)
echo "Good morning";; # 每個 statement 結束都必須用兩個分號
no)
echo "Good afternoon";;
y)
echo "Good morning";;
n)
echo "Good afternoon";;
*)
echo "sorry, answer not recongnized";;
esac
exit 0
#!/bin/bash
echo "Is it morning? Please answer yes or no"
read timeofday
case "$timeofday" in
yes | y | Yes | YES) # 可用 regular expression 一次篩選多個條件
echo "Good morning" # 可執行多個指令
echo "Ip bright and early this morning"
;; # 每個 statement 結束都必須用兩個分號
[nN]*)
echo "Good afternoon";;
*)
echo "sorry, answer not recongnized"
echo "Please answer yes or no"
exit 1
;;
esac
exit 0
LISTs
其實講白了,這個就是 AND & OR
#!/bin/bash
touch file_one
rm -f file_two
# AND 的用法
if [ -f file_one ] && echo "hello" && [ -f file_two ] && echo " there"
then
echo "in if(and)"
else
# 由於 file_two 已經刪除,因此執行到此處
echo "in else(and)"
fi
# OR 的用法
if [ -f file_one ] || echo "hello" || echo " there"
then
# 執行到此處,因為 -f file_one 為 true
echo "in if(or)"
else
echo "in else(or)"
fi
exit 0
Functions
function 的呼叫,除了必須注意是否有回傳值之外,還要注意 global/local variables 的問題! 以下用範例來說明:
沒有回傳值的使用範例:
有回傳值的使用範例:#!/bin/bash
sample_text="global variable" # 全域變數
foo() {
local sample_text="local variable" # 定義為區域變數
echo "Function foo is executing"
echo $sample_text # 區域變數
}
echo "script starting"
echo $sample_text # 全域變數
foo
echo "script ended"
echo $sample_text # 全域變數
exit 0
#!/bin/bash
# function 的宣告中不會帶參數,必須在呼叫時帶入
yes_or_no() {
echo "Is your name $* ?" # 印出帶入的參數
while true
do
echo -n "Enter yes or no: "
read x
case $x in
y | yes)
return 0;; # 0 表示回傳 true
n | no)
return 1;; # 1 表示回傳 false
*)
echo "Answer yes or no"
esac
done
}
echo "Original parameter are $*"
if yes_or_no "$1" # 帶入第一個參數
then
echo "Hi $1, nice name"
else
echo "never mind"
fi
exit 0
Commands
break
用法其實跟其他程式語言是相同的,作為跳出回圈之用,而 continue 的用法也相同。
#!/bin/bash
rm -rf fred*
echo > fred1
echo > fred2
mkdir fred3
echo > fred4
for file in fred*
do
if [ -d $file ]
then
break; # 效果同 C 語言中的 break
fi
done
echo "first directory starting fred was $file"
rm -rf fred*
exit 0
. (dot)
dot,透過 dot 會讓在 script 中的指令不會進入 sub shell 執行
# ========== dot.sh ==========
#!/bin/bash
PS2="###"
exit 0
# ========== 執行結果 ==========
# 原本的 $PS2
# shell> echo $PS2
# >
# 一般執行方式
# shell> sh dot.sh
# shell> echo $PS2 # 進入 sub shell 執行,因此還是沒有修改到 parent shell 中的設定
# >
# 以 dot 方式來執行
# shell> . dot.sh
# shell> echo $PS2
# ###
eval
將組合而成的字串變成有意義的變數
#!/bin/bash
foo=10
x=foo
y='$'$x
echo $y # 輸出 => $foo
# 使用 eval 來處理(類似 * 將 pointer 所代表的值提取出來的功能)
eval y='$'$x
echo $y # 輸出 => 10
export
在 sub shell 中定義環境變數之用
# ==================== export1.sh ====================
#!/bin/bash
foo="The first meta-syntactic variable"
export bar="The second meta-syntactic variable" # export 後即為環境變數
sh export2.sh
# ==================== export2.sh ====================
#!/bin/bash
echo $foo
echo $bar # 只有此行會顯示出內容,因為經過 export
expr
執行類似其他程式語言的基本數學運算處理
一般來說,在 shell programming 中是不能做數學運算的,因為 shell 把所有的值都視為字串(string),不過透過 expr 就可以解決這個問題,以下列出 expr 可以進行的簡單數學運算:
敘述 | 作用 |
expr1 | expr2 | 若 expr 不為 0,則回傳 expr1;反之回傳 expr2 |
expr1 & expr2 | 或兩者任何一個為 0,則回傳 0;反之回傳 expr1 |
expr1 = expr2 | 判斷是否相同 |
expr1 > expr2 | 判斷 expr1 是否大於 expr2 |
expr1 >= expr2 | 判斷 expr 是否大於等於 expr2 |
expr1 < expr2 | 派斷 expr1 是否小於 expr2 |
expr1 <= expr2 | 判斷 expr1 是否小於等於 expr2 |
expr1 != expr2 | 判斷 expr1 是否不等於 expr2 |
expr1 + expr2 | 回傳兩者相加的結果 |
expr1 - expr2 | 回傳兩者相減的結果 |
expr1 * expr2 | 回傳兩者相乘的結果 |
expr1 / expr2 | 回傳兩者相除的結果 |
expr1 % expr2 | 回傳餘數 |
接著用範例說明:
# x++
x = `expr $x + 1`
x = $(expr $x + 1)
printf
非常類似 C 語言中的 printf,但不支援浮點數的表示
shell> printf "%sn" Hello
Hello
shell> printf "%s %dt%s" "Hi there" 15 people
Hi there 15 people
set
在程式中設定參數
#!/bin/bash
echo the date is $(date)
# 設定參數(以 space 為區隔)
set $(date)
# 取第二個參數
echo The month is $2
exit 0
# ========== 執行結果 ==========
# shell> sh set.sh
# the date is 二 2月 26 15:10:00 CST 2008
# The month is 2月
shift
shift 參數之用,常用來瀏覽所有參數
#!/bin/bash
while [ "$1" != "" ]
do
echo $1
# $2->$1 $3->$2 $4->$3
shift
done
exit 0
# ========== 執行結果 ==========
# shell> sh shift.sh 1 2 3
# 1
# 2
# 3
trap
用來指定接收到特殊訊號時的處理動作
訊號(signal)的種類大致可分以下幾種:
訊號 | 描述 |
HUP (1) | Hang up (通常在 terminal 離線或使用者登出時會送出此類訊號) |
INT (2) | Interrupt (按下 Ctrl + C) |
QUIT (3) | Quit (按下 Ctrl + \) |
ABRT (6) | Abort (通常程式執行發生嚴重錯誤時會送出此類訊號) |
ALRM (14) | Alarm (通常用來處理逾時之用) |
TERM (15) | Terminate (通常當機器要關機時,由系統送出) |
#!/bin/bash
#若接收到 interrupt 的訊號,以下的命令就會執行
trap 'rm -f /tmp/my_tmp_file_$$' INT
echo creating file /tmp/my_tmp_file_$$
date > /tmp/my_tmp_file_$$
echo "press interrupt (Ctrl+C) to interrupt ...."
while [ -f /tmp/my_tmp_file_$$ ]
do
echo File exist
sleep 1
done
echo The file no longer exists
#即使接收到 interrupt 的訊號,檔案也不會被刪除
trap INT
echo creating file /tmp/my_tmp_file_$$
date > /tmp/my_tmp_file_$$
#因此此處會進入無限回圈
echo "press interrupt (Ctrl+C) to interrupt ...."
while [ -f /tmp/my_tmp_file_$$ ]
do
echo File exist
sleep 1
done
echo we never get here
exit 0
# ========== 執行結果 ==========
# shell> sh trap.sh
# creating file /tmp/my_tmp_file_10581
# press interrupt (Ctrl+C) to interrupt ....
# File exist
# File exist
# File exist <=== 此處按下 Ctrl + C
# The file no longer exists
# creating file /tmp/my_tmp_file_10581
# press interrupt (Ctrl+C) to interrupt ....
# File exist
# File exist
# File exist <=== 此處按下 Ctrl + C
unset
刪除自己設定的環境變數
#!/bin/bash
foo="Hello World"
echo $foo
# unset 後,foo 變數就不存在了
unset foo
echo $foo
# ========== 執行結果 ==========
# shell> sh unset.sh
# Hello World
# (此處為空白)
Beginning Linux Programming 學習筆記(2) - Shell Programming (Pipes & Redirection)
在說明 redirect 之前,必須知道 redirect 可以處理的,僅有 file descriptor 0(standard input), 1(standard output), 2(standard error) 三種,接著用例子說明使用方式:
# 將 ls -al 所產生的訊息輸出至檔案 lsoutput.txt 中(檔案內容會被覆蓋)
shell> ls -al > lsoutput.txt
# 將 ls -al 所產生的訊息附加至檔案 lsoutput.txt 中
shell> ls -al >> lsoutput.txt
# 使用可能會產生錯誤訊息的指令(例如 kill)
# 將 standard output 輸出至檔案 killout.txt 中
# 將 standard error 輸出至檔案 killerr.txt 中
shell> kill -HUP 1234 >killout.txt 2>killerr.txt
# 將 standard output & error 輸出到同一個檔案中
shell> kill -1 1234 >killouterr.txt 2>&1
# 不保留任何 standard output & error 相關訊息
shell> kill -1 1234 >/dev/null 2>&1
# 將 mydata.txt 檔案中的內容透過 more 指令呈現 (standard input)
shell> more < mydata.txt
Pipes
pipe 是將前一個 process 處理的結果交給下一個 process 繼續進行處理,藉而達到類似連結多個 process 的效果。
在 Linux 中,連接 process 的數量並沒有限制,並且還能跟 redirect 來搭配同時使用,以下用幾個範例來說明:
# (1) 取得 ps 相關資訊
# (2) 排序後將結果存入檔案 pssort.txt 中
shell> ps | sort > pssort.txt
# (1) 取得 ps 相關資訊
# (2) 排序
# (3) 使用 more 來展示
shell> ps | sort | more
# (1) 取得 ps 相關資訊
# (2) 排序
# (3) 過濾掉重複的 process name
# (4) 清除名稱為 sh 的 process
# (5) 使用 more 來展示
shell> ps -xo comm | sort | uniq | grep -v sh | more
# (1) 使用 cat 讀取 mydata.txt 的內容
# (2) 將內容進行排序
# (3) 過濾掉重複的資料,並將結果存回 mydata.txt 中
shell> cat mydata.txt | sort | uniq > mydata.txt
2008年2月20日 星期三
Beginning Linux Programming 學習筆記(1) - Library 簡介
撰寫 C 語言時,除了必須用的 header file 之外,另外一個重要角色,就是 library 了! 而 library 又分為兩種:
- static library (副檔名為 .a)
- shared library (副檔名為 .so.N)
Static Library
所謂的 static library,其實沒很特別,只是一堆 object file 的集合而已,以下介紹 static library 的用法。
假設有兩個 function 分別位於不同的 file 中:
另外設計一支程式,使用到上面所定義的 function:// ==================== fred.c ====================
#include <stdio.h>
void fred(int arg) {
printf("fred: we passed %d\n", arg);
} //end fred
// ==================== bill.c ====================
#include <stdio.h>
void bill(char *arg) {
printf("bill: we passed %s\n", arg);
} //end bill
接著是編譯的方式,有幾種方式可以進行編譯:// ==================== program.c ====================
#include <stdlib.h>
#include "lib.h"
int main(void) {
bill("Hello World!");
exit(0);
} //end main
# (1) 直接將主程式與 function file 一同編譯其中第三種是比較標準的作法,不過其實 gcc 的功能很強,所以在編譯的時候可以省略一些指令卻可以達到相同效果。
shell> gcc -o program program.c bill.c
# (1) 將定義兩個 function 的檔案編譯成 object file
# (2) 將主程式與 object file 一同進行編譯
shell> gcc -c fred.c bill.c ; gcc -o program program.c bill.o
# (1) 將定義兩個 function 的檔案編譯成 object file
# (2) 產生 static library (archive)
# (3) 將主程式與 static library 一同進行編譯
shell> gcc -c fred.c bill.c ; ar -crv libfoo.a fred.o bill.o ; gcc -o program program.c libfoo.a
# (1) 將定義兩個 function 的檔案編譯成 object file
# (2) 產生 static library (archive)
# (3) 將主程式與 static library 一同進行編譯(-L 指定 library directory、-l 指令所要用的 library name)
shell> gcc -c fred.c bill.c ; ar -crv libfoo.a fred.o bill.o ; gcc -o program program.c -L. -lfoo
Shared Library
談到 shared library 之前,必須先說明 static library 的缺點,就是當很多程式都使用相同的 static library 時,會在 memory 中產生多份相同的 copy,無形之中造成記憶體很多浪費,因此才會有 shared library 的出現。
shared library 存放的位置跟 static library 大致上是相同的,不過副檔名為 .so.N (N 為重新修改的次數)。
使用 shared library,即時同時被多個程式所使用,系統也只要在 memory 中存有一份資料即可,比起 static library 是節省記憶體許多。
而其運作的方式,在一開始時,shared library 是不會被載入 memory 中,而是當主程式執行時(此時主程式被載入 memory 中),將主程式所使參照使用到的 share library 在執行期間載入 memory 中執行。
接著,用來處理主程式與 shared library 參照工作的程式,稱為 ld.so(或是 ld-linux.so.N),而相關設定檔位於 /etc/ld.so.conf 中,若是設定有任何改變,則需要執行 ldconfig 指令重建參照的工作。
最後,若要查詢一個程式執行時所需求的 shared library,可使用以下指令:
shell> ldd program
linux-gate.so.1 => (0xffffe000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7e89000) # 需求 libc.so 這個 shared library
/lib/ld-linux.so.2 (0xb7fdf000)
2007年11月26日 星期一
修改 Ubuntu runlevel 設定
結果發現,在 Ubuntu 中竟然沒有 /etc/inittab 這個檔案........(忘記從哪一版開始沒有,不過我用 6.10 就已經沒有了!)
因此 google 的一下修改方式,找到以下這邊討論串:
由於解說蠻簡單的,也就不翻譯了,直接節錄修改方式:
1./etc/inittab.看了以上的修改方式,就是不讓 GDM 啟動就對了!
You're right. It's gone. It left as of Edgy Eft, as Ubuntu now manages start-up services with "upstart."
The file does exist in pre-Edgy Ubuntu's, and other flavors of Linux, so you were not crazy to look there.
2. default runlevel in Ubuntu
In redhat/suse, the default run level is 5. I came from RH, and like you, expected the same in Ubuntu. After the "upstart" transition, the notion of runlevels is supposedly gone, but if forced to choose, the default run level is actually 2 in Ubuntu, not 5.
2. You don't want GNOME to start at boot, how?
So yes, in other flavors of Linux, you'd probably just change run levels, because on run level would have X, and another wouldn't. But in Ubuntu, *all* run levels start GNOME, so here's an easier way:
In /etc/rc2.d, you will see a file S13gdm. This starts the GNOME system.
Simply change that file to K13gdm.
# sudo mv S13gdm K13gdm
(It's possible the number between the "S" and the "gdm" will be slightly different than 13. That's OK.)
This leaves the package installed, but just not started at boot.
To start X later, run:
# sudo /etc/init.d/gdm start.
不過應該還有更好的修改方式,之後有看到再說吧!!
2007年9月17日 星期一
2007年9月6日 星期四
2007年9月4日 星期二
Linux SATA HowTo
此外,若想要清楚了解目前 Linux 已經支援了哪些 SATA chipset,可以參考以下兩個網站,資料整理的相當詳細:
- Serial ATA (SATA) chipsets — Linux support status
- Serial ATA (SATA) Linux hardware/driver status report (此網頁記錄每個版本的 kernel 所新加入支援的 SATA chipset 有哪些!)
因此在購買硬體前,事先進行確認,可以避免掉後續許多硬體不支援的麻煩。
但若是因為一時不察,買到了內建目前各大 Linux distribution 都不支援的 SATA chiset 的產品(例如:主機板、SATA RAID Card .... 等等),雖然麻煩了點,但還是可以透過幾個方式解決:
1、進入 BIOS 設定為相容模式
在 BIOS 中尋找 SATA 的設定,假設在 IBM X60 上,可以找到 Config -> Serial ATA(SATA),設定為 Compatibility。
在其他不同廠牌的 BIOS 中,可能會有 Legacy ATA Mode 可供設定!
透過此種方式,BIOS 會將 SATA Device 視為 PATA 處理,當然效能會有所折損,不過可以在順利安裝 Linux 後,可以透過以下方式讓 Linux 支援 SATA 的裝置:
- 透過線上更新將 kernel 換成最新版的
在 Fedora 中可以使用 yum;在 Ubuntu 中可以使用 apt。kernel 更新完成後,可能就支援原本不支援的 SATA 了,但也有可能一樣不支援! - 重新編譯 kernel
若是在 kernel 官方網站中,發現其實在新版的 kernel 中已經支援了該 SATA 裝置,就可以下載 kernel 的 source code 後,開啟 SATA 的支援並重新進行編譯,如此一來就可以正確驅動該 SATA chipset,接著置換原本的 kernel,就可以解決 SATA 裝置無法驅動的問題。而編譯 kernel 的方式可以參考此篇文章(鳥哥的 Linux 私房菜 - 核心編譯)。
2、使用廠商提供的 SATA chipset driver
若是很清楚 SATA chipset 是哪家廠商所生產的,並找到該廠商所提供的官方版 driver,就可以在安裝時,使用「linux dd」指令在安裝前載入 SATA chipset driver,如此一來安裝時,就可以正確的驅動 SATA device。
PS. 其實此種解決方式跟在 Windows 的模式是差不多的,在安裝 Windows 前若要載入 SATA driver,必須按 F6;只是在 Linux 的安裝中,必須下達指令「linux dd」。
結論
假設廠商沒有提供任何的 driver,線上更新 kernel 後也還是不支援,甚至在 Linux kernel 官方網站中都沒有提供支援該 SATA chipset 的計劃,就表示目前該 SATA chipset 在 Linux 是無法正常運作的,因此可能要重新購買其他已經支援的產品。
參考資訊
2007年8月13日 星期一
將 MP3 ID3 資訊轉為 Unicode 編碼
為什麼聽 mp3 時,檔名都沒辦法正確顯示呢?
答案就是因為在 Linux 中的音樂播放軟體,顯示的不是歌曲的名稱,而是 ID3 資訊,不過通常 ID3 資訊都不是以 Unicode 編碼(因為 Windows 搞出來的 MP3 的 ID3 都不會是 Unicode 編碼),因此顯示出來都會是亂碼...
那.......要怎麼處理呢?
在 Windows 上,可以用 ConvertZ 去轉換! 這一套軟體很強,檔名可以轉,ID3 資訊也可以轉。
在 Linux 上,檔名可以用 iconv 轉換,而 ID3 資訊可以用 python-mutagen 套件轉換!
以下說明轉換 ID3 資訊的方式,假設 ID3 編碼為 Big5,只要進入要轉換的目錄,下達以下指令即可:
shell> find . -iname "*.mp3" -execdir mid3iconv -e Big5 {} ;如此一來就解決 ID3 編碼非 Unicode 的問題囉!
PS. 還是 Unicode 比較好.......畢竟支援多國語言嘛!
參考資訊
2007年7月25日 星期三
[Ubuntu] 使用 apt 進行套件升級發生「failed to write cache」的錯誤
Updating fontconfig cache...
/usr/share/fonts: failed to write cache
/usr/share/fonts/X11: failed to write cache
/usr/share/fonts/X11/100dpi: failed to write cache
/usr/share/fonts/X11/75dpi: failed to write cache
/usr/share/fonts/X11/Type1: failed to write cache
/usr/share/fonts/X11/encodings: failed to write cache
/usr/share/fonts/X11/encodings/large: failed to write cache
......... (more)
......... (more)
這時老大來救命,找到了以下 script 來解決了這個問題:
#!/bin/bash就是將上面的內容作成 shell script 執行一次就行了!
#
# 修正 Ubuntu 7.04 fc-cache failed to write cache 錯誤
#
sudo touch /usr/share/fonts
sudo touch /usr/share/fonts/X11
sudo touch /usr/share/fonts/X11/100dpi
sudo touch /usr/share/fonts/X11/75dpi
sudo touch /usr/share/fonts/X11/Type1
sudo touch /usr/share/fonts/X11/encodings
sudo touch /usr/share/fonts/X11/encodings/large
sudo touch /usr/share/fonts/X11/misc
sudo touch /usr/share/fonts/X11/util
sudo touch /usr/share/fonts/truetype
sudo touch /usr/share/fonts/truetype/arphic
sudo touch /usr/share/fonts/truetype/baekmuk
sudo touch /usr/share/fonts/truetype/freefont
sudo touch /usr/share/fonts/truetype/kochi
sudo touch /usr/share/fonts/truetype/openoffice
sudo touch /usr/share/fonts/truetype/thai
sudo touch /usr/share/fonts/truetype/ttf-arabeyes
sudo touch /usr/share/fonts/truetype/ttf-bengali-fonts
sudo touch /usr/share/fonts/truetype/ttf-bitstream-vera
sudo touch /usr/share/fonts/truetype/ttf-dejavu
sudo touch /usr/share/fonts/truetype/ttf-devanagari-fonts
sudo touch /usr/share/fonts/truetype/ttf-gentium
sudo touch /usr/share/fonts/truetype/ttf-gujarati-fonts
sudo touch /usr/share/fonts/truetype/ttf-kannada-fonts
sudo touch /usr/share/fonts/truetype/ttf-lao
sudo touch /usr/share/fonts/truetype/ttf-malayalam-fonts
sudo touch /usr/share/fonts/truetype/ttf-mgopen
sudo touch /usr/share/fonts/truetype/ttf-oriya-fonts
sudo touch /usr/share/fonts/truetype/ttf-punjabi-fonts
sudo touch /usr/share/fonts/truetype/ttf-tamil-fonts
sudo touch /usr/share/fonts/truetype/ttf-telugu-fonts
sudo touch /usr/share/fonts/type1
sudo touch /usr/share/fonts/type1/gsfonts
sudo touch /usr/share/X11/fonts
sudo touch /usr/share/X11/fonts/100dpi
sudo touch /usr/share/X11/fonts/75dpi
sudo touch /usr/share/X11/fonts/Type1
sudo touch /usr/share/X11/fonts/encodings
sudo touch /usr/share/X11/fonts/encodings/large
sudo touch /usr/share/X11/fonts/misc
sudo touch /usr/share/X11/fonts/util
sudo touch /usr/local/share/fonts
sudo touch /var/lib/defoma/fontconfig.d
sudo touch /var/lib/defoma/fontconfig.d/A
sudo touch /var/lib/defoma/fontconfig.d/B
sudo touch /var/lib/defoma/fontconfig.d/C
sudo touch /var/lib/defoma/fontconfig.d/D
sudo touch /var/lib/defoma/fontconfig.d/E
sudo touch /var/lib/defoma/fontconfig.d/F
sudo touch /var/lib/defoma/fontconfig.d/G
sudo touch /var/lib/defoma/fontconfig.d/H
sudo touch /var/lib/defoma/fontconfig.d/J
sudo touch /var/lib/defoma/fontconfig.d/K
sudo touch /var/lib/defoma/fontconfig.d/L
sudo touch /var/lib/defoma/fontconfig.d/M
sudo touch /var/lib/defoma/fontconfig.d/N
sudo touch /var/lib/defoma/fontconfig.d/O
sudo touch /var/lib/defoma/fontconfig.d/P
sudo touch /var/lib/defoma/fontconfig.d/R
sudo touch /var/lib/defoma/fontconfig.d/S
sudo touch /var/lib/defoma/fontconfig.d/T
sudo touch /var/lib/defoma/fontconfig.d/U
sudo touch /var/lib/defoma/fontconfig.d/V
sudo touch /var/lib/defoma/fontconfig.d/a
sudo touch /var/lib/defoma/fontconfig.d/j
sudo touch /var/lib/defoma/fontconfig.d/m
sudo touch /var/lib/defoma/fontconfig.d/u
2007年6月17日 星期日
bash 學習筆記 - 流程控制
if/else
if/else 的語法如下:
if condition
then
statements
[elif condition
then statements...]
[else
statements]
fi
Exit Status
與其他程式語言(C、C++、Pascal....等等)不同的地方,在 condition 的部分,其他的程式語言是以 condition 為 True or False 來決定要執行哪一段程式;但在 bash 中卻不是如此!
在 bash 中,就必須按照 Linux 的運作方式來進行處理,因此 condition 是否成立取決於 command 的 exit status。
而在 Linux 的所有指令,不論是由何種程式語言開發,執行完後都會回傳一個整數值給呼叫此程式的 process,此整數值即稱為 exit status;一般來說,「0」代表程式執行成功,而「1~255」則代表程式執行失敗。
有了 exit status 的概念後,if/else 的語法就可以修改如下:
if command ran successfully
then
normal processing
else
error processing
fi
Return
這邊要探討的是一般程式語言都有提供的 return 功能,其實說穿了,return N 即是 exit N(Exit Status 為N),不過比較不一樣的是,return 僅能用於 function 中,且要執行含有 function 的 shell script,必須透過 source 的方式來執行才有效。
總而言之,在 function 中使用 return 就跟在其他程式語言的方式差不多。
同時使用多個 Exit Status
或許有人會覺得,如果條件判斷中需要同時判斷兩個條件以上,要如何作呢? 在 bash 中當然也辦的到,就像其他程式語言一樣,bash 也可以針對判斷條件進行 &&(AND) 或是 ||(OR) 運算,來決定是否執行特定程式,以下用個簡單的範例說明:
filename=$1
word1=$2
word2=$3
if grep $word1 $filename || grep $word2 $filename
then
echo "$word1 or $word2 is in $filename."
fi
各種狀況的判斷
當然除了靠 Exit Status 外,還有許多不同的情況會發生,例如:字串比對、檔案屬性的檢查、數字比較....等等,以下逐一來進行介紹各種比對方式。
字串的比對:
Operator | 成立條件 |
str1 = str2 | 這不多解釋了.... |
str1 != str2 | 這不多解釋了.... |
str1 < str2 | 這不多解釋了.... |
str1 > str2 | 這不多解釋了.... |
-n str1 | 當 str1 不為 null 時 |
-z str1 | 當 str1 為 null 時(長度為 0) |
檔案屬性:
Operator | 成立條件 |
-a file | 檔案存在 |
-d file | 檔案存在,並且是個目錄 |
-e file | 檔案存在 |
-f file | 檔案存在,且為一般檔案(非目錄或其他特殊型態) |
-r file | 目前的使用者有讀取檔案的權限 |
-s file | 檔案存在且內容非空白 |
-w file | 目前的使用者有寫入檔案的權限 |
-x file | 目前的使用者有執行檔案(or 進入目錄)的權限 |
-N file | 檔案自從上一次被讀取後,有被修改過 |
-O file | 檔案的 owner 為目前的使用者 |
-G file | 檔案的 group 為目前使用者所屬之 group |
file1 -nt file2 | file1 比 file2 的日期新 |
file1 -ot file2 | file1 比 file2 的日期舊 |
如果使用上面的判斷時,最好加上「中括號」,變成類似以下的格式:
if [ condition ] && [ condition ]; then當然也可以跟 Exit Status 結合,就會變成類似以下的格式:
if command && [ condition ]; then以下來段簡單的程式(節錄自書):
當然,兩兩比較一定會有數字的比對,以下介紹數字比較的方式:# 檢查檔案是否不存在
if [ ! -e "$1" ]; then
echo "file $1 does not exist."
exit 1
fi
# 檢查是否為目錄
if [ -d "$1" ]; then
echo -n "$1 is a directory that you may "
if [ ! -x "$1" ]; then
echo -n "not "
fi
echo "search."
elif [ -f "$1" ]; then # 檢查是否為檔案
echo "$1 is a regular file."
else # 檢查是否為特殊型態的檔案
echo "$1 is a special type of file."
fi
# 檢查目前使用者是否為 owner
if [ -O "$1" ]; then
echo 'you own the file.'
else
echo 'you do not own the file.'
fi
# 檢查是否有讀取檔案的權限
if [ -r "$1" ]; then
echo 'you have read permission on the file.'
fi
# 檢查是否有寫入檔案的權限
if [ -w "$1" ]; then
echo 'you have write permission on the file.'
fi
# 檢查第一個參數是否有執行的權限,且非目錄
if [ -x "$1" -a ! -d "$1" ]; then
echo 'you have execute permission on the file.'
fi
Keyword | 效果 |
-lt | Less than (小於) |
-le | Less then or equal (小於或等於) |
-eq | Equal (等於) |
-ge | Greater than or equal (大於或等於) |
-gt | Greater than (大於) |
-ne | Not equal (不等於) |
for
for loop 在 bash 中當然也是不可欠缺的一員大將啦! 只是跟其他程式語言不同的地方,他並非以「數字」的方式直接指定 for loop 的程式要執行的次數,而是以一個串列的值(例如:1 3 4 6 8)去執行,有幾個值就執行幾次,因此格式大概如下:
for name [in list ]其實說穿了,使用方式就跟其他程式語言中的 for each 是差不多相同的,以下結合 if/else 判斷來個範例程式:
do
statements that can use $name...
done
#!/bin/bash
IFS=":"
for dir in $PATH
do
if [ -z "$dir" ]; then
dir="."
fi
if ! [ -e "$dir" ]; then
echo "${dir} doesn't exist"
elif ! [ -d "${dir}" ]; then
echo "${dir} diesn't a directory"
else
ls -ld $dir
fi
done
另外再舉個例子,將 function、for、if/else 通通結合進來,下面這個程式的目的是以 recursive 的方式將參數中目錄底下的所有目錄列出,以下是程式內容:
有興趣知道輸出結果的,可以 copy 回去自己試試看。echoDir() {
chrTab="${chrTab}t"
cd $1
for eachFilePath in $(ls)
do
if [ -d $eachFilePath ]; then
echo -e ${chrTab}${eachFilePath}
echoDir $eachFilePath
fi
done
cd ..
chrTab=${chrTab%'t'}
}
filePath=$1
chrTab=""
if [ -d $1 ]; then
cd $1
for eachFilePath in $(ls)
do
if [ -d $eachFilePath ]; then
echo $eachFilePath
echoDir $eachFilePath
fi
done
cd ..
fi
case
case 在 bash 中的用法其實就是跟 C、Java 等程式語言中的 switch case 差不多,以下是使用語法:
case expression in以下用個簡單程式來說明:
pattern1 )
statements ;;
pattern2 )
statements ;;
...
esac
for filename in "$@"; do
case $filename in
*.jpg | *.png ) echo "may be jpg or png" ;;
*.tga ) echo "tga" ;;
*.xpm ) echo "xpm" ;;
* ) echo "can not identified file type."
esac
done
select
這個部分就跟一般的程式語言很不一樣了! 而且僅在 bash 1.14 版以後才有支援喔! 透過 select,可以很容易的產生出選單,以下是使用 select 的語法:
select name [in list ]透過 select 語法,可以將 list 中的內容變成一個個的數字選項供使用者選擇,並且有兩個變數需要注意到,分別是 $name 以及 $REPLY;其中 $name 變數的值為選項的內容,而 $REPLY 的值則是選項所代表的數字。
do
statements that can use $name...
done
當使用者輸入某選項的數字時,$name 與 $REPLY 就會存入相對應的值,但若是輸入一個非選項內的數字,則兩個變數的值都會變成 null,因此可以透過檢查 $name 變數值是否為 null 來判斷使用者有無輸入合法的選項。
以下用一個簡單的小程式來作範例:
至於 select 要如何應用在許許多多的管理工作上,就需要管理者發揮創意了!#!/bin/bash
# 指定分隔 $PATH 的字元
IFS=":"
# 變數 PS3 儲存使用 select 時提示使用者的字串
PS3="which path you want to use? "
select mySelect in $PATH;
do
if [ $mySelect ]; then
echo "mySelect = ${mySelect}"
echo "REPLY = ${REPLY}"
else
echo "no such option!"
# 透過關鍵字「break」就可離開了!
break;
fi
done
# 以下是執行結果:
1) /usr/kerberos/sbin 4) /usr/local/bin 7) /usr/sbin
2) /usr/kerberos/bin 5) /sbin 8) /usr/bin
3) /usr/local/sbin 6) /bin 9) /root/bin
which path you want to use? 1
mySelect = /usr/kerberos/sbin
REPLY = 1
which path you want to use? 5
mySelect = /sbin
REPLY = 5
which path you want to use? 10
no such option!
while & until
在 while 與 until 的部分,也是跟一般程式語言中的用法差不多,以下分別介紹 while 與 until 的語法:
while
while condition dountil
statements... done
until command ; do statements... done其實說穿了,while 與 until 不過就是一體兩面的用法而已,差別僅在於 while 判斷條件是否為 true,until 判斷條件是否為 false。
以下用兩段程式來說明,以下兩段程式的作用是相同的,只是分別以 while 與 until 來撰寫:
until [ cp $1 $2 ];
do
echo 'Attempt to cpoy failed. waiting...'
sleep 5
done
這裡僅是簡單用法的介紹,其他就靠大家發揮了!while [ ! cp $1 $2 ];
do
echo 'Attempt to cpoy failed. waiting...'
sleep 5
done
2007年6月16日 星期六
[NFS] 解決「mount request from unknown host」的問題
其中有三台主機:
- NFS Server:192.168.0.1/24
- NFS Client1:192.168.0.10/24
- NFS Client2:192.168.1.10/24
先說明一下原本在 NFS server 上的設定內容,如下:
/var/share/nfs/glite 192.168.*(rw,async,all_squash,anonuid=1000)
在此設定中,Client 1 是可以正確 mount NFS partition,但是 Client 2 卻失敗了! 在 Client 2 上面顯示了類似以下的訊息:
mount: 192.168.0.1:/var/share/nfs/glite failed, reason given by server: Permission denied說也奇怪,只有在網段外的機器會有這種情形喔......
不過沒關係,此時我連到 Server 翻 log.....翻到類似以下訊息:
mount request from unknown host 192.168.1.10 for /var/share/nfs/glite (/var/share/nfs/glite)啊......明明我在 server 就有設定進去啊! 怎麼會變成 unknown host ?
這跟本也不會是 firewall 的問題....(因為 showmount -e 是可以看到資料的!)
試了半天,終於發現問題所在,原來在 server 上不能這樣設定,應該要把設定改為如下:
/var/share/nfs/glite 192.168.0.0/16(rw,async,all_squash,anonuid=1000)就是上面藍字的部分,改成那樣後再重新啟動 NFS server,問題就解決了.....
2007年6月10日 星期日
基本 bash 程式設計 (4) - Command Substitution
- script 中指定
- 以 position parameter 的方式指定
在這個部分中,要介紹更強大好用的變數指定方式,稱為 Command Substitution !
Command Substitution 的神奇之處在於「可以將各種命令的 standard output 作為變數的值」! 使用語法如下:
$(指令內容)以下用一些範例來說明:
# 列出目前所在的目錄
echo $(pwd)
# 列出使用者家目錄的檔案
echo $(ls $HOME)
# 列出目前所在目錄下的檔案
echo $(ls $(pwd))
# 檔案中的斷行字元會被移除掉後印出其內容
echo $(< sort_data.txt)
接著再來個稍微複雜一些的,假設從主機中發信給目前登入主機的所有使用者,可以這樣做:
最後,其他更加靈活的使用,就是在於管理者的創意與巧思了!# 顯示目前所有登入 server 的使用者
who
# 取得所有使用者的清單
who | cut -d' ' -f1
# 寄信給他們囉!
mail $(who | cut -d' ' -f1)
2007年6月3日 星期日
基本 bash 程式設計 (3) - String Operators
String Substitution
在宣告變數時,有可能會因為疏忽而打錯;或是在寫給使用者使用的 script,每次都要詳細指定參數的麻煩....
其實這些錯誤或麻煩都可以避免,只要稍微修改一下變數的使用方式(搭配符號「{ }」)即可,以下用一張圖表來說明:
Operator | 作用 | 目的 | 範例 |
${varname:-word} | 假如 varname 變數存在,並且不是 null,則回傳變數內容;反之則回傳 word | 用於設定變數預設值 | ${count:-0} 若變數 count 未定義,則回傳 0 |
${varname:=word} | 假如 varname 變數存在,並且不是 null,則回傳變數內容;反之則設定此變數的值為 word,再回傳變數的值 | 用於將未定義的變數設定預設值 | ${count:=0} 若變數 count 未定義,則設定 count 變數的值為 0 並回傳 |
${varname:?message} | 假如 varname 變數存在,並且不是 null,則回傳變數內容;反之則顯示「varname: message」,並中斷目前的命令或 script。 | 用來針對未定義的變數進行 debug | ${count:?"undefined!"} 若變數 count 未定義,則會顯示「count: undefined!」,並中斷程式執行 |
${varname:+word} | 假如 varname 變數存在,並且不是 null,則回傳 word | 用來測試變數是否存在 | ${count:+1} 若變數 count 存在,則回傳 1 |
${varname:offset:length} | 回傳 varname 變數的值中,從第 offset 的字元開始(變數值的第一個位置為 0),長度為 length 的字串。 有以下幾種用法:
| 取得變數所儲存字串的部分字串 | count="frogfootman" ${count:4} => footman ${count:4:4} => foot |
像是表格中的第一種用法,就相當適用於必須為變數指定預設值的情形下。
以下直接用個例子來說明使用方式,假設我們檔案「sort-data.txt」,內容如下:
8 eight接著要寫一支 script 來根據最前面的數字作排序,並顯示指定的行數:
9 nine
1 one
2 two
3 three
7 seven
5 five
6 six
4 four
0 zero
執行結果如下:#!/bin/bash
# 檔案名稱:highest.sh
# 檢查 file 名稱是否有輸入
fileName=${1:?"filename missing, please check your file."}
# 設定預設顯示的行數
# 若使用者未輸入第二個參數就預設為 5
displayLine=${2:-5}
# sort 是用來排序的程式
# 參數「n」指定以數字為依據作排序
# 參數「r」則是 reverse 的意思,由大排到小
# head 是用來取「前幾行」
sort -nr $fileName | head -$displayLine
# 沒指定檔名時
shell> sh highest.sh
highest.sh: line 5: 1: filename missing, please check your file.
# 未指定所要顯示的行數
shell> sh highest.sh sort-data.txt
9 nine
8 eight
7 seven
6 six
5 five
# 指定顯示行數
shell> sh highest.sh sort-data.txt 3
9 nine
8 eight
7 seven
Patterns & Pattern Matching
說到字串處理,怎麼可以缺乏正規表示式呢?
當然 bash 中提供的不是正規表示式,不過提供的也是類似的功能,以下用一個表格來介紹:
Operator | 作用 |
${variable#pattern} | 從字串開頭開始比對,當 pattern 符合變數中的值,即不需繼續比對,僅比對至最短的符合字串,刪除比對到的部分並回傳剩下的變數值 |
${variable##pattern} | 同上,會回傳刪除比對後的變數值,但所採取的是貪婪比對,會一直比對到最長的部分為止 |
${variable%pattern} | 從字串尾端開始比對,當 pattern 符合變數中的值,即不需繼續比對,僅比對至最短的符合字串,刪除比對到的部分並回傳剩下的變數值 |
${variable%%pattern} | 同上,會回傳刪除比對後的變數值,但所採取的是貪婪比對,會一直比對到最長的部分為止 |
${variable/pattern/string} | 差異處: 在 variable 與 pattern 比對後,在上面的 operator 中,只有第一個比對到的字串會被更換為 string;在下面的 operator 中則會取代所有比對到的字串。 相同處:
|
${variable//pattern/string} |
以下直接用範例來說明:
繼續提到上面說的 pattern,有更進階的用法,可以進行多重比對,只要把每個 pattern 以符號「|」相隔即可,以下用表格來說明:myPath="/home/cam/book/long.file.name"
# 將兩個「/」之間的內容都刪除(貪婪比對)
echo ${myPath##/*/} # long.file.name
# 效果同上(因為 * 也可以代表 /)
echo ${myPath##*/} # long.file.name
# 將兩個「/」之間的內容刪除
echo ${myPath#/*/} # cam/book/long.file.name
# 從尾端開始比對,將比對符合結果刪除
echo ${myPath%.*} # /home/cam/book/long.file
# 從尾端開始比對,將比對符合結果刪除(貪婪比對)
echo ${myPath%%.*} # /home/cam/book/long
myPhotoName="myphoto.png"
# 將副檔名 png 改為 jpg
echo ${myPhotoName%.png}.jpg # myphoto.jpg
myEnvPath="/home/user/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin"
# 將「:」全部換成斷行字元,結果如下:
# /home/user/bin
# /usr/local/bin
# /bin
# /usr/bin
# /usr/X11R6/bin
echo -e ${myEnvPath//:/'\n'}
Operator | 說明 | 範例 | 效果 |
*(PatternList) | 符合 0 個到多個指定的 pattern 清單 | *(alice|hatter|hare) | 字串中出現 alice 或是 hatter 或是 hare,甚至完全沒有,都符合 |
+(PatternList) | 符合 1 個到多個指定的 pattern 清單 | +(alice|hatter|hare) | 字串中出現 alice 或是 hatter 或是 hare 都符合 |
+[0-9] | 僅數字符合 | ||
?(PatternList) | 符合 0 個到 1 個指定的 pattern 清單 | ?(alice|hatter|hare) | 字串中僅出現一次 alice 或是 hatter 或是 hare,或是完全沒有,就符合 |
@(PatternList) | 剛好完全符合指定 pattern 清單的其中 1 個 | @(alice|hatter|hare) | 僅有 alice、hatter、hare 這三個字串符合(精確比對) |
!(PatternList) | 完全不符合指定的 pattern 清單 | !(alice|hatter|hare) | 只要字串中沒有 alice、hatter、hare 即符合 |
!(vt+([0-9])) | 只要字串中沒有「vt+數字」的部分即符合 |
至於如何使用,就要靠使用者的創意與巧思了!
Length Operator
最後,要來介紹如何取得變數值的長度,這很簡單,語法如下:
${#varname}以下來個簡單的範例:
myFile="myfile.txt"
echo ${#myFile} # 輸出 10