1:Redirect Operators and File Descriptors QUOTE: &n redirect standard output to file descriptor n nfile redirect file descriptor n to file n>>file redirect file descriptor n to file. Create file if non-existent, else overwrite. n>| file redirect file descriptor n to file. Create file even if noclobber is enabled. n&m redirect file descriptor n output to file descriptor m nfile open file for reading and writing as file descriptor n n&- close file descriptor n for standard output print &un args redirect arguments to file descriptor n. If n is greater than 2, it must first be opened with exec. If n is not specified, the default file descriptor argument is 1 (standard output). read &un args read input line from file descriptor n. If n is greater than 2, it must first be opened with exec. If n is not specified, the default file descriptor argument is 0 (standard input). 2: A=B B=$A 這樣,B 的變量值就可繼承 A 變量"當時"的變量值了 这段文字我看不太懂,,网中人可不可以再详解一下,特别是"当时"指的是什么 也就是 command line 碰到 $A 時,會先提換出 $A 的值(當時的值),也就是 B 。 於是,B=$A 就是 B=B 了... 3: 单引号‘’ 取消除单引号以外的任何字符的特殊含义。如:echo ‘my name is $name’其结果为:my name is $name ,此时$只作为一个普通字符使用了。 双引号“” 取消除双引号、$号以及_号以外的所有字符的特殊含义 4: && 和 || 其作用是使管道线有条件地执行。其使用方法为: command1 && command2:当且仅当command1执行成功后才执行command2 command1 || command2:当且仅当command1执行失败后才执行command2 && 與 || 差在哪? 好不容易,進入兩位數的章節了... 一路走來,很辛苦吧?也很快樂吧? ^_^ 在解答本章題目之前,先讓我們了解一個概念:return value ﹗ 我們在 shell 下跑的每一個 command 或 function ,在結束的時候都會傳回父行程一個值,稱為 return value 。 在 shell command line 中可用 $? 這個變量得到最"新"的一個 return value ,也就是剛結束的那個行程傳回的值。 Return Value(RV) 的取值為 0-255 之間,由程式(或 script)的作者自行定議: * 若在 script 裡,用 exit RV 來指定其值,若沒指定,在結束時以最後一道命令之 RV 為值。 * 若在 function 裡,則用 return RV 來代替 exit RV 即可。 Return Value 的作用,是用來判斷行程的退出狀態(exit status),只有兩種: * 0 的話為"真"( true ) * 非 0 的話為"假"( false ) 舉個例子來說明好了: 假設當前目錄內有一份 my.file 的文件,而 no.file 是不存在的: CODE:
$ touch my.file $ ls my.file $ echo $? # first echo 0 $ ls no.file ls: no.file: No such file or directory $ echo $? # second echo 1 $ echo $? # third echo 0 上例的第一個 echo 是關於 ls my.file 的 RV ,可得到 0 的值,因此為 true ﹔ 第二個 echo 是關於 ls no.file 的 RV ,則得到非 0 的值,因此為 false ﹔ 第三個 echo 是關於第二個 echo $? 的 RV ,為 0 的值,因此也為 true 。 請記住:每一個 command 在結束時都會送回 return value 的﹗不管你跑甚麼樣的命令... 然而,有一個命令卻是"專門"用來測試某一條件而送出 return value 以供 true 或 false 的判斷, 它就是 test 命令了﹗ 若你用的是 bash ,請在 command line 下打 man test 或 man bash 來了解這個 test 的用法。 這是你可用作參考的最精確的文件了,要是聽別人說的,僅作參考就好... 下面我只簡單作一些輔助說明,其餘的一律以 man 為準: 首先,test 的表示式我們稱為 expression ,其命令格式有兩種: CODE:
#!/bin/bash
my_fun() {
echo '$0 inside function is '$0
echo '$1 inside function is '$1
echo '$2 inside function is '$2
}
echo '$0 outside function is '$0
echo '$1 outside function is '$1
echo '$2 outside function is '$2
my_fun fp1 "fp2 fp3"
然後在 command line 中跑一下 script 就知道了:
CODE:
chmod +x my.sh
./my.sh p1 "p2 p3"
$0 outside function is ./my.sh
$1 outside function is p1
$2 outside function is p2 p3
$0 inside function is ./my.sh
$1 inside function is fp1
$2 inside function is fp2 fp3
然而,在使用 positional parameter 的時候,我們要注意一些陷阱哦:
* $10 不是替換第 10 個參數,而是替換第一個參數($1)然後再補一個 0 於其後﹗
也就是,my.sh one two three four five six seven eigth nine ten 這樣的 command line ,
my.sh 裡的 $10 不是 ten 而是 one0 哦... 小心小心﹗
要抓到 ten 的話,有兩種方法:
方法一是使用我們上一章介紹的 ${ } ,也就是用 ${10} 即可。
方法二,就是 shift 了。
用通俗的說法來說,所謂的 shift 就是取消 positional parameter 中最左邊的參數( $0 不受影響)。
其預設值為 1 ,也就是 shift 或 shift 1 都是取消 $1 ,而原本的 $2 則變成 $1、$3 變成 $2 ...
若 shift 3 則是取消前面三個參數,也就是原本的 $4 將變成 $1 ...
那,親愛的讀者,你說要 shift 掉多少個參數,才可用 $1 取得 ${10} 呢? ^_^
okay,當我們對 positional parameter 有了基本概念之後,那再讓我們看看其他相關變量吧。
首先是 $# :它可抓出 positional parameter 的數量。
以前面的 my.sh p1 "p2 p3" 為例:
由於 p2 與 p3 之間的 IFS 是在 soft quote 中,因此 $# 可得到 2 的值。
但如果 p2 與 p3 沒有置於 quoting 中話,那 $# 就可得到 3 的值了。
同樣的道理在 function 中也是一樣的...
因此,我們常在 shell script 裡用如下方法測試 script 是否有讀進參數:
CODE:
#!/bin/bash
my_fun() {
echo "$#"
}
echo 'the number of parameter in "$@" is '$(my_fun "$@")
echo 'the number of parameter in "$*" is '$(my_fun "$*")
然後再執行 ./my.sh p1 "p2 p3" p4 就知道 $@ 與 $* 差在哪了 ... ^_^
11:這次的題目之前我在 CU 的 shell 版已說明過了:
$ mail -s test root
this is a test mail.
please skip.
^d (同時按 crtl 跟 d 鍵)
很明顯,mail 程式所讀進的數據,就是從 stdin 也就是 keyboard 讀進的。
不過,不見得每個程式的 stdin 都跟 mail 一樣從 keyboard 讀進,
因為程式作者可以從檔案參數讀進 stdin ,如:
CODE:
$ ls my.file no.such.file >/dev/null
ls: no.such.file: No such file or directory
那接下來,假如單純只跑程式,不想看到任何輸出結果呢?
哦,這裡留了一手上次節目沒講的法子,專門贈予有緣人﹗... ^_^
除了用 >/dev/null 2>&1 之外,你還可以如此:
CODE:
if 判斷式的例子很常見,你可從很多 shell script 中看得到,我這裡就不再舉例子了...
接下來要為大家介紹的是 case 判斷式。
雖然 if 判斷式已可應付大部份的條件執行了,然而,在某些場合中,卻不夠靈活,
尤其是在 string 式樣的判斷上,比方如下:
CODE:
QQ () {
echo -n "Do you want to continue? (Yes/No): "
read YN
if [ "$YN" = Y -o "$YN" = y -o "$YN" = "Yes" -o "$YN" = "yes" -o "$YN" = "YES" ]
then
QQ
else
exit 0
fi
}
QQ
從例中,我們看得出來,最麻煩的部份是在於判斷 YN 的值可能有好幾種式樣。
聰明的你或許會如此修改:
CODE:
...
if echo "$YN" | grep -q '^[Yy]\([Ee][Ss]\)*$'
...
也就是用 Regular Expression 來簡化代碼。(我們有機會再來介紹 RE)
只是... 是否有其它更方便的方法呢?
有的,就是用 case 判斷式即可:
CODE:
QQ () {
echo -n "Do you want to continue? (Yes/No): "
read YN
case "$YN" in
[Yy]|[Yy][Ee][Ss])
QQ
;;
*)
exit 0
;;
esac
}
QQ
我們常 case 的判斷式來判斷某一變量在同的值(通常是 string)時作出不同的處理,
比方說,判斷 script 參數以執行不同的命令。
若你有興趣、且用 Linux 系統的話,不妨挖一挖 /etc/init.d/* 裡那堆 script 中的 case 用法。
如下就是一例:
CODE:
case "$1" in
start)
start
;;
stop)
stop
;;
status)
rhstatus
;;
restart|reload)
restart
;;
condrestart)
[ -f /var/lock/subsys/syslog ] && restart || :
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
exit 1
esac
(若你對 positional parameter 的印像已經模糊了,請重看第 9 章吧。)
okay,十三問還剩一問而已,過幾天再來搞定之.... ^_^
13:for what? while 與 until 差在哪?
終於,來到 shell 十三問的最後一問了... 長長吐一口氣~~~~
最後要介紹的是 shell script 設計中常見的"循環"(loop)。
所謂的 loop 就是 script 中的一段在一定條件下反覆執行的代碼。
bash shell 中常用的 loop 有如下三種:
* for
* while
* until
for loop 是從一個清單列表中讀進變量值,並"依次"的循環執行 do 到 done 之間的命令行。
例:
CODE:
for var in one two three four five
do
echo -----------
echo '$var is '$var
echo
done
上例的執行結果將會是:
1) for 會定義一個叫 var 的變量,其值依次是 one two three four five 。
2) 因為有 5 個變量值,因此 do 與 done 之間的命令行會被循環執行 5 次。
3) 每次循環均用 echo 產生三行句子。
而第二行中不在 hard quote 之內的 $var 會依次被替換為 one two three four five 。
4) 當最後一個變量值處理完畢,循環結束。
我們不難看出,在 for loop 中,變量值的多寡,決定循環的次數。
然而,變量在循環中是否使用則不一定,得視設計需求而定。
倘若 for loop 沒有使用 in 這個 keyword 來指定變量值清單的話,其值將從 $@ (或 $* )中繼承:
CODE:
for var; do
....
done
(若你忘記了 positional parameter ,請溫習第 9 章...)
for loop 用於處理"清單"(list)項目非常方便,
其清單除了可明確指定或從 positional parameter 取得之外,
也可從變量替換或命令替換取得... (再一次提醒:別忘了命令行的"重組"特性﹗)
然而,對於一些"累計變化"的項目(如整數加減),for 亦能處理:
CODE:
for ((i=1;i
除了 for loop ,上面的例子我們也可改用 while loop 來做到:
CODE:
num=1
while [ "$num" -le 10 ]; do
echo "num is $num"
num=$(($num + 1))
done
while loop 的原理與 for loop 稍有不同:
它不是逐次處理清單中的變量值,而是取決於 while 後面的命令行之 return value :
* 若為 ture ,則執行 do 與 done 之間的命令,然後重新判斷 while 後的 return value 。
* 若為 false ,則不再執行 do 與 done 之間的命令而結束循環。
分析上例:
1) 在 while 之前,定義變量 num=1 。
2) 然後測試(test) $num 是否小於或等於 10 。
3) 結果為 true ,於是執行 echo 並將 num 的值加一。
4) 再作第二輪測試,其時 num 的值為 1+1=2 ,依然小於或等於 10,因此為 true ,繼續循環。
5) 直到 num 為 10+1=11 時,測試才會失敗... 於是結束循環。
我們不難發現:
* 若 while 的測試結果永遠為 true 的話,那循環將一直永久執行下去:
CODE:
while :; do
echo looping...
done
上例的" : "是 bash 的 null command ,不做任何動作,除了送回 true 的 return value 。
因此這個循環不會結束,稱作死循環。
死循環的產生有可能是故意設計的(如跑 daemon),也可能是設計錯誤。
若要結束死尋環,可透過 signal 來終止(如按下 ctrl-c )。
(關於 process 與 signal ,等日後有機會再補充,十三問暫時略過。)
一旦你能夠理解 while loop 的話,那,就能理解 until loop :
* 與 while 相反,until 是在 return value 為 false 時進入循環,否則結束。
因此,前面的例子我們也可以輕鬆的用 until 來寫:
CODE:
num=1
until [ ! "$num" -le 10 ]; do
echo "num is $num"
num=$(($num + 1))
done
或是:
CODE: