17. /dev/null
$ ls my.file no.such.file 2>/dev/null
my.file
若要相反:只想看到 stderr 呢?還不簡單﹗將 stdout 弄到 null 就行:
$ ls my.file no.such.file >/dev/null
ls: no.such.file: No such file or directory
除了用 >/dev/null 2>&1 之外,你還可以如此:
$ ls my.file no.such.file &>/dev/null
(提示:將 &> 換成 >& 也行啦~~! )
再問:那... 有辦法不取消而又"臨時"蓋寫目標檔案嗎?
$ set -o noclobber
$ echo "6" >| file.out
$ cat file.out
6
留意到沒有:在 > 後面再加個" | "就好(注意: > 與 | 之間不能有空白哦)....
再來還有一個難題要你去參透的呢:
$ echo "some text here" > file
$ cat < file
some text here
$ cat < file > file.bak
$ cat < file.bak
some text here
$ cat < file > file
$ cat < file
---- 怎麼最後那個 cat 命令看到的 file 竟是空的?﹗
$ cat < file > file 之後原本有內容的檔案結果卻被洗掉了﹗
這只是 priority 的問題而已
* 在 IO Redirection 中,stdout 與 stderr 的管道會先準備好,纔會從 stdin 讀進資料。
file 會先將 file 清空,然後纔讀進 < file ,
但這時候檔案已經被清空了,因此就變成讀不進任何資料了...
在 pipe line 之間,前一個命令的 stderr 是不會接進下一命令的 stdin 的,
其輸出,若不用 2> 導到 file 去的話,它還是送到監視器上面來﹗
這點請你在 pipe line 運用上務必要注意的。
* 在 cm1 | cm2 | cm3 ... 這段 pipe line 中,若要將 cm2 的結果存到某一檔案呢?
若你寫成 cm1 | cm2 > file | cm3 的話,
那你肯定會發現 cm3 的 stdin 是空的﹗(當然啦,你都將水管接到別的水池了﹗)
cm1 | cm2 > file ; cm3 < file
cm1 | cm2 > file ; cm3 < file
有的,那就是 tee 命令了。
* 所謂 tee 命令是在不影響原本 I/O 的情況下,將 stdout 複製一份到檔案去。
因此,上面的命令行可以如此打:
cm1 | cm2 | tee file | cm3
18. 你要 if 還是 case 呢?
comd1 && {
comd2
comd3
} || {
comd4
comd5
}
若你記得 return value ,我想你也應該記得了 && 與 || 是甚麼意思吧?
用這兩個符號再配搭 command group 的話,我們可讓 shell script 變得更加聰明哦。
假如 comd1 的 return value 爲 true 的話,
然則執行 comd3 與 comd4 ,
否則執行 comd4 與 comd5 。
改成if then else
if comd1
then
comd2
comd3
else
comd4
comd5
fi
可使用 elif 這樣的 keyword :
if comd1; then
comd2
elif comd3; then
comd4
else
comd5
fi
若 comd1 爲 true ,然則執行 comd2 ﹔
否則再測試 comd3 ,然則執行 comd4 ﹔
倘若 comd1 與 comd3 均不成立,那就執行 comd5 。
雖然 if 判斷式已可應付大部份的條件執行了,然而,在某些場合中,卻不夠靈活,
尤其是在 string 式樣的判斷上,比方如下:
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
else
exit 0
fi
}
從例中,我們看得出來,最麻煩的部份是在於判斷 YN 的值可能有好幾種式樣。
..
if echo "$YN" | grep -q '^[Yy]\([Ee][Ss]\)*$'
QQ () {
echo -n "Do you want to continue? (Yes/No): "
read YN
case "$YN" in
[Yy]|[Yy][Ee][Ss])
;;
*)
exit 0
;;
esac
}
我們常 case 的判斷式來判斷某一變量在同的值(通常是 string)時作出不同的處理,
比方說,判斷 script 參數以執行不同的命令。
若你有興趣、且用 Linux 系統的話,不妨挖一挖 /etc/init.d/* 裏那堆 script 中的 case 用
法。
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
18.最後要介紹的是 shell script 設計中常見的"循環"(loop)。
bash shell 中常用的 loop 有如下三
* for
* while
* until
for loop 是從一個清單列表中讀進變量值,並"依次"的循環執行 do 到 done 之間的命令行。
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 ((i=1;i<=10;i++))
do
echo "num is $i"
done
除了 for loop ,上面的例子我們也可改用 while loop 來做到:
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 的話,那循環將一直永久執行下去:
* 與 while 相反,until 是在 return value 爲 false 時進入循環,否則結束。
因此,前面的例子我們也可以輕鬆的用 until 來寫:
num=1
until [ ! "$num" -le 10 ]; do
echo "num is $num
num=$(($num + 1))
done
或是:
代碼:
num=1
until [ "$num" -gt 10 ]; do
echo "num is $num"
num=$(($num + 1))
done