UNIX Shell Programming


Google

http://www.freebsd.org.hk/html/other/shell1.html

Chap 0 -- 前言 ( UNIX Shell Programming not for guru ...... )
(0.1) �者需要那些基� ?
�篇文章的主�是 UNIX Shell Programming . ���篇文章�要一�
基� , 包括 : � UNIX 的命令要有一��� , 尤其是常用 , 重要的
命令 . 此外 , 你也必需�在 UNIX 上使用 editor , 像 vi , joe 或
emacs 等 ...... �然 , 假如你有�程式的�� , 那� UNIX Shell
Programming �你��不�是反掌折枝�了 .
(0.2) 使用那一� Shell ? Why ?
既然 Shell 有很多� , 我�所要介�是最早由 AT&T Bell Labs 的
Stephen Bourne 所�的�� Shell , 也就是所�的 Bourne Shell .
在一些情�下 , 也��便提一下 C Shell 相�的�西 .
既然�在已�有了更新更好的 Shell , 那我��什��要介� Bourne
Shell 呢? �是因� , 在 UNIX Shell Programming 中 , 有�多都
�是用 Bourne Shell 的�法�成的 , 尤其像 /etc/rc.X ( SunOS )
而一般使用者或�用 C Shell 的增�版 tcsh 或 bash ( Bourne Again
Shell ) �些比�新的 Shell . 不��有�� , 只要系�中有 sh
( Bourne Shell ) ��直�器存在 , 那� , 我�都可以用 Bourne
Shell 的�法��程式 . ( �有 sh 的系� , 是很�想像的 ...... )
�有一� , 用 sh ��程式��上比用 csh 要好 , �路上已�有
���了 , 很多�也都提到用 csh �程式的缺� , �建�不要用
csh ��程式 . 我�人�有什�意� , sh � csh 就好比程式�言
中的 Pascal � C (只是比喻) , 想必������言的人都知道 ,
只要��了其中一� , 另一�也不算太�� , sh � csh 也是一� .
�且�程式能真正作事就可以 , 不需要一下子用 sh , 一下子用 csh
��程式 .
*************************************************************
注意 !!!! 所有的例子都要在 Bourne Shell 系列的�境下操作
建�先看看 2.3 , 才不�搞了半天 , 才��原�你是用 C Shell
系列的 Shell (Shell 的��及系列可�考 2.3)
此外 , 文中的一些例子都是在 Linux 下操作 , 在 SunOS 或 HP
上 , 命令也�有少�的不同 . �你在演�� , 假如不能正�的
�行 , 你��看看指令的 manual . 像 SunOS 的 find � Linux
的 find 就有差� . 大部份的例子都可以在各�不同的平�上�行 .
*************************************************************
(0.3) 本篇文章的架�
本�想用 html ��的 , ��似乎比�好 , 但是�得有�麻� , 所
以�是用平常的格式�� , 各位假如用 joe , 那��看到 (�考 x.y)
� , 你�是可以用 ctrl-k f ��找 x.y 的�� ; 在 vi 下也可以用
/x.y ��找 , ��也�也可以�到 hypertext 的效果吧 ...... :)
Chap 1 -- �介
Chap 2 -- 基本知�
Chap 3 -- Shell Programming 中的�料表示
Chap 4 -- Shell Programming 中的�法
Chap 5 -- �取�料及程式��
附�一 -- Shell Summary 及 一些 Shell Script 的例子
附�二 -- grep �介及 regular expressions
�考�料 -- 介�一些相�的�及�料
(0.4) 假如你已�� Bourne Shell Programming 很熟 ......
那也�附�一 Shell Summary ��你有��及快速�考的好� !
�有 , �考�料中的�目 , 都是值得一看的� , 假如你��看� ,
那我想你可以�考看看 . �然 , man 及 grep 的配合 , 也常常
可以助你一臂之力 .
Chap 1 -- �介
�疑的 , UNIX 是一�功能�大的作�系� , 它多人多工特性 , �路
的支援 , 多采多姿的 X Windows System , 都相�的吸引人 . 除此之外
UNIX �有一�重要的特性 , 就是它提供了�以百�的命令 .
每�人都知道 , DOS 下面也不�二三十�命令 , �二三十�命令可能
一�天就可以�的很好了 ; 但 UNIX 作�系��不然 , �百�命令就算
都��了 , 若我�不知道如何去 "�合�些命令" , 那我��失去��
UNIX 的最大�趣 !!
有人��得疑惑 : "�合命令"�有趣� ? 什�叫作 "�合命令" ?
�就是我�所要��的 . �在就�一�最��的例子而言 : ��算目前
工作目�下共有多少�普通�案 ( Regular File ) ?
�你遇到��的�� , 你要如何解� ? 有人�� : " 我用 ls -l 列
出目前工作目�下所有的�案 , 然後慢慢的� ...... " , 假如你用了
��的方法 , 而目前工作目�下又有�百��案 , 那� , 我真的很敬
佩你 "愚公移山" 的精神 . �有些人比� "暴力" , 他� : " ��嘛
很�� , 我�一� C �言程式 , 用 opendir , lstat 等函���算
某一目�下有多少�普通�案 ...... " 假如你要��做 , 那我�然不
反� , 常���程式也是不�的 . 然而 , 就�了��小小的一���
而�一段 C �言的程式 , 未免太小�大作 . ��上 , 解答的��就
在 : " 你是不是���百� UNIX 的命令中挑出�合的命令 , 然後把
�些命令�合起� , 以�到我�所要的目的 !! " . " UNIX 的每�命令
就好比各�不同的 IC , 每� IC 都有它特殊的作用 , 藉由不同 IC 的
�合 . 我�可以�到我�所要的功能 " , " 要�到目的的方法 , 常常
不是只有一� ......" 上面��句�相�重要 , 各位�在也��不清
楚 . 不�上面�句�所提的 , 的�就是 UNIX ��吸引人的地方之一 .
除了命令之外 , Shell 提供了一些�似一般程式�言的�法 , 我�可
以利用�些�法��程式 . 而��也正是本篇文章的最主要�容 .
Chap 2 -- 基本知�
(2.1) 什�是 Shell ?
我�可以把 UNIX 切成�方面�看 , 一部份是系�的核心 (kernel) ,
另一部份就是系�中的�用程式 . �凡我�常用的一些命令 , 像
ls , find � Shell 一� , 都可被����用程式 . 所以 , Shell
的地位�其它的�用程式��有什�差� .
我�可以看看�入 UNIX 作�系�的�程 : �我��入 username 之
後 , getty 就�束 , 取而代之的是 login . 在我��入正�的
password 後 , 系�就依照 /etc/passwd 中的�� , �使用者��
相��的 Shell . 在���候 , 使用者就�得到一�提示符� .
接著我�就可以下�一些命令 .
--  getty --> login --> shell --> logout --
^                                          |
|------------------------------------------
Shell 有另一�名�叫做 "命令列直�器" (command interpreter)
���名��中 , 我�就可以了解到 Shell ��上�命令列的翻�官
�我�在提示符�後�入一�串的命令 , �按下 Enter 之後 , Shell
就�我�所�入的�西 , 做一���的分析及�理 .
(2.2) Shell ��的事
�系�做交�性的使用 . 包含了�出�入�向 ( �考 2.5 ) ,
管� ( �考 2.6 ) Wildcard 的展�及匹配 ( �考 2.4 ) , 另外
�有 Job Control( � SVR4 �始) , ���境�定?. 解�命令 ....
�有一�很重要 , Shell 有它自己�建的程式�言 , 利用它的�法
我�可以��程式 , �出�程式功能�不像一般的程式�言 ( 如 C
�言 ) 那�多 , 然而 , 在很多情�下就�用了 . �部份也就是整
篇文章的重� .
(2.3) UNIX 系�上 Shell 的��
在 DOS 中 , 想必各位一定都知道它的 Shell � command.com . 特�
要�明的是 : " Shell 是可以被置�掉的 ! " 所以 , �多人在 DOS
中 , 常常把他�的 Shell 由 command.com �成 4dos.com , 以得到
更多的功能 , �已�是很平常的事了 . 在 UNIX 系�中也是一� ,
使用 chsh ��命令 , 你可以挑你喜�的 Shell �使用 . 合法的
Shell 清�被列在 /etc/shells ���案中 .
Shell 的��可以分���支派 , 一�由 Bourne Shell 衍生而�的
包括了 sh (Bourne Shell) , ksh (Korn Shell) , bash (Bourne
Again Shell) , zsh (Z Shell) ; 另一支派� C Shell 衍生而�的 ,
包括了 csh , tcsh . �在�多人作�的�境常常是在 csh 或 tcsh
之下 , 尤其是� BSD 衍生而� SunOS 的�境 .
你可以使用 echo $SHELL ��知你�在到底使用那一� Shell . 或
者直接看 /etc/passwd 的最後一��位 .
(2.4) Wildcard 的展�及匹配
Wildcard 有些人把它翻�成 "�用字元" , 下面所列�的
�目 , �不�然每一� Shell 都支援 , 各位要自己��看 .
(2.4.1) * : 代表任意的字串 ( 字串可以是空的 )
% ls
haha    haha1   haha2   memo1   memo13  memo2   memo23
% ls haha*
haha   haha1  haha2
% ls memo*
memo1   memo13  memo2   memo23
(2.4.2) ? : 代表任意一�字元
% ls
memo1   memo13  memo2   memo23
% ls memo?
memo1  memo2
% ls memo1?
memo13
(2.4.3) []: 代表符合[]中所列�的字元
% ls
haha    haha1   haha2   memo1   memo13  memo2   memo23
% ls memo[12]
memo1  memo2
[a-e]bc �符合 abc , bbc , cbc , dbc , ebc

(2.4.4) [!]: [] 中的字元 , 若加了 ! , �表示不符合[]中所列�的字元
% ls
haha    haha1   haha2   memo1   memo13  memo2   memo23
% ls memo[!1]
memo2
[!a-z]bc �符合不是以小�英文字母�� , 後面接著 bc
的字串 . 如�符合 : Cbc , 6bc 等 ......
(2.4.5) {word1,word2....} : 如 my_{dog,cat,pig} �符合 my_dog
my_cat , my_pig

(2.4.6) \ : 接在 \ 之後的特殊字元 , 其特殊的意��被取消
例如 , 有一��案 , 它的名字真的叫 *abc , 那我�得用
cat \*abc 才能把 *abc 中的�容�示出� .

(2.4.7) 更多�於 Wildcard 的�� :
(I)  glob : �� Wildcard 匹配的�作 , 我��� " globbing "
在很多� Shell 之中都有一�叫作 noglob 的�� , 可以把
Wildcard 展�匹配的�作�掉 .
% ls *bc
abc  bbc  cbc  dbc

% set noglob    output_file
�� > 的符�就是�向符� .
下表是一般�常看到的�出�入�向 , prog 就是 program 的意思
而 file 就是一��案:
功能                    sh,ksh,bash         csh,tcsh
--------------------------------------------------------------
把���出�到一��案        prog > file         prog > file
把���出�到�案描述值 n    prog >&n
把原��出至�案描述值 m      prog m>&n
的�出��出至�案描述值 n
的�出 , 一起�向至�案描
述值 n
把�����出�到一��案    prog 2> file
把���出��                prog >&-
把���出以及�����出    prog > file 2>&1    prog >& file
�到一��案
把���出�到 f1 , 把��   (prog > f1) 2>f2   (prog > f1) >& f2
���出�到 f2
--------------------------------------------------------------
把���出�到一��案的後面    prog >> file        prog >> file
把�����出�到一��案的    prog 2>> file
後面
把���出以及�����出�    prog >> file 2>&1   prog >>& file
到一��案的後面
--------------------------------------------------------------
把�案�作���入            prog  file2
�入 , �把���出的�果
再�向至 file2
--------------------------------------------------------------
���的�入�作 stdin , 直     prog  file
w > who_log
把 w ��指令的�出�到一�叫做 who_log 的�案
cat file1 > file2
^^^^^^^^^
把 cat file1 的�出�到一�叫做 file2 的�案
(II) prog 2> file
先�一�叫做 test1.c 的 C �言程式如下 :
#include
void main(void)
{
print("Standard Error Test");   /* 故意�� */
}
接著�入  cc test1.c               errmsg
此� , 原本在�端�上的���出就�被�到 errmsg ���案中

�有 , prog 2> /dev/null 可以抑制���息的�出 , 因�它把
�����出�到 /dev/null ��垃圾桶中 , ��功能�上面的
prog >&- 有相同的地方 , 本����出的�息都被�抑了 . 只
不�一�是抑制���息�出 , 一�是抑制正常�息�出 . 不�
prog 2>&- 就可以�到� prog 2> /dev/null 相同的效果 .
(III) prog > file 2>&1
我�可以��下面的命令 :
find / -size +2000k > ~/output 2>&1
接著你可以在你的 Home Directory 下找到 output ���案 , 你
可以看看它的�容 . 你就��� , 有些目��你��是
Permission denied 的 , �些都是���息的�出 . 你也�看到
有些�案名 , �些�案都是大於 2000k 的 , �於正常�息的�出
所以 , output ���案的�包含著���出以及�����出的
�息 .
** 注意 !!! SunOS 上的 find 是以 block ��位 , 所以 , 假
如你在 SunOS 上操作 , 那�把 +2000k 的 k 去掉 , 如下 :
find / -size +2000 > ~/output 2>&1

(IV) (prog > f1) 2>f2
再一次的 , 我�用上面的命令 , 但改� :
(find / -size +2000k > ~/normal_out) 2>/tmp/error_out
在 (III) 中 , 我�把正常�息的�出����息的�出都混在
~/output ���案中 . �某�角度�� , �不是很好的作法 ,
我�所希望的 , 是把正常�息的�出放在一��案 , 而��
�息的�出放到另一��案 . 此� , 我�就要�用 (IV) ��
方法 . 在上面的命令中 , 我�把 find ��命令的正常�息�出
�到 ~/normal_out ���案 , 而���息�出我�就把它�到
/tmp/error_out ���案中 .

(V) 至於 prog >> file  ,  prog 2>> file  ,  prog >> file 2>&1
� prog > file  ,  prog 2> file  ,  prog > file 2>&1
只是把 > �成 >>  , 而它的差�就只在 :  >> 是"附加"的意思
�不像 > �把原��向�去的�案清除 (假如欲�向�去的�案
已�存在 , 而且 noclobber ���有被�定的� !�然 , 若原�
�案不存在 , 那就��造出新的�案)
所以 , ��看下面的命令就可明白 :
ls -la /usr > file1
ls -la /etc > file1
cat file1        file2
ls -la /etc >> file2   file2
��型式是由���向符�所�成 , 看起�有��� , 其�一�也
不 . 只要你拆成�部份�看 : prog  file2
^^^^^^^^^^^^            (i)
^^^^^^^^^^^^^^^^^^^^    (ii)
第 (i) 部份 : 把 file1 �作是 prog 的���入
第 (ii) 部份 : 把第 (i) 部份的�出�果 , �向至 file2 中
��例子 :
tr 'a-z' 'A-Z'  dest
首先 , tr 'a-z' 'A-Z'  dest 的� , 我��在
�端�上看到��後的�出 . 然而 , 我�使用 > dest 再把原��
�在�端�上的�出�向到 dest ���案中 . 所以 , dest 就�
是 sour ����後的�容 .
(VIII) prog  this is a test
> but ....
> End_Of_Letter              This is Here Document Test
> This Sample is quite typical
> You will see the same style in some Shell Script
> See You
> It_is_OK
如此作可以一次�示一大段�息 , 而不必很麻�的用�多列 echo
�做 .
(2.6) 管� (Pipes)
管�就是�成 "�合命令" 的方法 , �端重要 ! 它的型式是���子
prog1 | prog2
其中 , | 就是管�的符� , 上面的意思是 : 把 prog1 的�出 , �
做 prog2 的�入 . 你也可以想像成 : prog1 及 prog2 是���器 ,
| 是它�之�的�送� , �� prog1 �理�的�西 , 再交� prog2
去加工 . ���例子 :
ls -la | more    [1-5]) echo 'haha';;
> [6-10]) echo 'lala';;
> esac
haha
你自己也可以��看 , 不�你得使用 Bourne Shell 系列的 Shell
好了 , 我�已�看到可以在提示符�下�程式 , 然而 , 我��不
喜���做 . 因�在提示符�下要修改程式很麻� , 小的程式也就
算了 , 大的程式我想�有人�喜���方法 . 所以 , 在一般的情
�下 , 我�可以先用文���器�好一�程式�存成一��案 , 然
後再把���案交� Shell 去�行 , 而���案就�� Shell
Script ! 先�各位看看一�最��的 Shell Script :

#!/bin/sh
ps -a | more
上面的�列�容 , 我把它放在一��案中 , 然後直接�行它就可以.
但不要忘� , 你要����案有�以及�行的�限 .
(3.2) Shell Programming 的�大要素 : �料及�法
假如你��高��言 , 你��可以了解到 , 其�每�高��言的概
念都差不多 , 它�只是�法不同 , 也��料表示的方法也有少�的
差� . 但是 , ��了一�再去����不算� .
Shell Programming 也是一� , 它�是有大家熟悉的�法如 if , do
for , while 等等的�西 . 比�特殊的是它的�料型� .
下面就�始�入 Shell Programming 的�料表示 . �法�看 Chap 4
(3.3) �解及 #!/bin/sh
很多程式�言都有其�解格式 , 在一� Shell Script 中 , 假如我
�看到一� # � , 那� # ��始一直到那列的�尾都是�解 .
�解只是提高程式的可��度 , � Shell ���不�做解�的�作
�一� Shell Script 如下:
#!/bin/sh
#
# Count_bash_user_number : report how many users use bash
#
grep -c '/bin/bash' /etc/passwd    # grep -c : print match
# number
�� Shell Script 中 , 真正作事的只有一列 , 其它 # 後接的文
字都是�解 . 但特�要注意的是 : #!/bin/sh 不是�解 , �然它
是以 # ��� , 但是它有特殊的意� . 在最早的�候因�只有 sh
所以也�不需要 #!/bin/sh , 但是後� Shell 的��越�越多 ,
有些人用 csh , 有些人用 tcsh . ��候我�就要特�的指出 , �
� Shell Script 是用那一� Shell �的 . 假如你的 Shell Script
是以 Borune Shell 的�法�成的 , 那你就得加上 #!/bin/sh , 假
如是以 C Shell 的�法�成的 , 那你得加上 #!/bin/csh . �列一
定要加� ? 答案是不�然 , 如果你目前使用的 Shell �你�的
Shell Script 使用相同的�法 , 那可以不加 . 但是我�一�例子:
假如你用的是 tcsh , 但�用 sh 的�法�� Shell Script , 而�有
在 Shell Script 的第一列加上 #!/bin/sh , 那���生什�情形?
很�� , tcsh �以�你�的 Shell Script 是用 tcsh 的�法�成
的 , 所以在解�的�程中就�出��� . 但是若我�有加 #!/bin/sh
那� , tcsh 就�叫用 sh �解�你的 Shell Script , 自然就不�
有���生 .
我的建�是 : "��如何 , �明�的在 Shell Script 的第一列指出
�� Shell Script 是用那一� Shell 的�法�成的 !"
(3.4) ��
(3.4.1) ��的指定
如同大部份的程式�言一� , Shell Programming Language 中也有
��的存在 , 那我�要如何指定一���呢 ? 很�� , 如下:
variable=value
���例子 :
count=1
X11_bin_path=/usr/X11/bin
但在指定��� , 有��要注意的 : 第一 , 等�的左右��不要
有空格 , 譬如� , 不可�成 : color = blue , color= blue
color =blue , 一定得�成 : color=blue . 第二 , ���有所�
的 "�料型�" . �你指定��� , ��只是被�成字元的�合�
看待 . 如上面的例子 : count=1 , count 放的是��的 1 , 而不
像 C �言中所� "整�型�的 1" 第三 , ��指定的�作��上
也可以在提示符�後直接作 . ( 如同 2.7 所提到的 )
(3.4.2) ��的�容 : $variable
使用 echo $variable 可以�示��的�容 .
像��的例子 , count=1 , 那� , 使用 echo $count 就�得到 1
使用 echo $X11_bin_path 就�得到 /usr/X11/bin
所以 , 我�可以了解到 : $variable 是��的�容 !
我�在�一��� , 各位��看看 : 把 var1 的�容�定成 10 ,
把 var2 的�容�定成 20 , 接著把 var2 的�容�定� var1 .
var1=10
var2=20
var1=$var2
echo $var1
再����一下 , 下面的四列�上面的四列有何不同 ?
var1=10
var2=20
var1=var2
echo $var1
(3.4.3) ��� Wildcard
你可以在提示符�下���� :
list=*
ls $list
上面的�列 , 首先把 * 指定� list , 所以 $list 就是 *
那 ls $list 就��成 ls *
five_char=?????
ls $five_char
上面�列�把�度�五�字元的�案列出 .
(3.4.4) ${variable}
有人�了 (3.4.3) 中的例子之後就想到 , 我�可不可以��做 :
file=mydoc
cp $file $filenew
把 mydoc �案��一份 , �把新�案的名�後加上 new �成
mydocnew . 假如你按照上面的方法�作 , �有���生 .
�是因� , $file 固然是 mydoc �� , 然而 $filenew �是
一��容不知道是什�的�� . 所以我�����做 :
file=mydoc
cp ${file} ${file}new
其中 ${file} ��成 mydoc , ${file}new ��成 mydocnew
(3.5) 引�
在 Shell Programming 中 , 很特殊的一�就是引� , 引�有三�
分�� ' ' �引�  , " " �引� , ` ` 反�引� . 它�分�在
Shell Programming 中扮演不同但都相�重要的角色 . 我�分��
述如下 :
(3.5.1) �引�
�引�最大的用�是 : 使得���引�之�所�的�容保持不� .
比如�你想印出 : pig   cat      dog
那假如你��作 : echo pig   cat     dog
那��果仍是 pig cat dog
要改成 : echo 'pig   cat      dog'  才能�到目地
�有 , 在�引�中的特殊符�也�失去意� :
% echo '* is all'
* is all

% color=blue
% echo 'Is $color beautiful?'
Is $color beautiful?
% echo '> >  >  ?"
There are 1 arguments
argument1 is *  ?
argument2 is
argument3 is
% argtest *
There are 64 arguments
argument1 is a.c
argument2 is c.c
argument3 is e.c
% argtest `cat friend_list`
There are 3 arguments
argument1 is veronica
argument2 is jhhsu
argument3 is ghguo
(3.7.3) $* 代表所有的��
把��的 Shell Script 再加以�充如下 :
#!/bin/sh
echo "There are $# arguments"
echo "argument1 is $1"
echo "argument2 is $2"
echo "argument3 is $3"
echo "all arguments : $*"
拿�� Shell Script ���看 :
% argtest a b c
There are 3 arguments
argument1 is a
argument2 is b
argument3 is c
all arguments : a b c
% argtest 'a b c'
There are 1 arguments
argument1 is a b c
argument2 is
argument3 is
all arguments : a b c
% argtest "a b c"
There are 1 arguments
argument1 is a b c
argument2 is
argument3 is
all arguments : a b c
% argtest *
There are 7 arguments
argument1 is haha
argument2 is haha1
argument3 is haha2
all arguments : haha haha1 haha2 memo1 memo13 memo2 memo23
(3.7.4) ��的移� shift
前面提� , $n 是第 n ���的�容 , 但我�要特�注意的是 , n
必需小於 10 , 也就是� , 不可能有 $10 ��的�西 . 那假如我
���的��很多 , 超�了 10 � , 而又想取得超� 10 �後的�
� , 要如何做 ? 答案就在 shift .
shift 是一���的命令 , 它把 $n 的�容放到 $n-1 中 . 如 $2
放到 $1 , $3 放到 $2 ...... 但 $1 �不�放到 $0 , 而是永�的
消失 . 看下面的 Shell Script 及�行�果 :
#!/bin/sh
echo "There are $# arguments"
echo "all argument : $*"
shift
echo "There are $# arguments"
echo "all argument : $*"
shift
echo "There are $# arguments"
echo "all argument : $*"
shift
echo "There are $# arguments"
echo "all argument : $*"
% argtest a b c d e f g h i j
There are 10 arguments
all argument : a b c d e f g h i j
There are 9 arguments
all argument : b c d e f g h i j
There are 8 arguments
all argument : c d e f g h i j
There are 7 arguments
all argument : d e f g h i j
所以�上面的例子得知 , 每 shift 一次 , 我�就可以在 $9 的地方
得到原�的第 10 ����容 . 如此一� , 要取得第 10 �以後的�
�就一�也不困�了 . �有 , $1 �然�在 shift 後被�掉 , 但假
如 $1 以後�要用到的� , 我�仍然可以在 shift 前�它保留起� :
.
.
.
temp=$1
shift
.
.
.


Chap 4 -- Shell Programming 中的�法
在 Chap 3 �完之後 , 相信各位已�可以�一些小的程式 . 但是�
好像少了些什� ? 不� , �些可�是每� Programming Language
中的�魂 : �法 ! �法包含了�件��的判� , �圈 , 函�呼叫
等 ......有了�些 , Shell Programming Language 才算完整 .
(4.1) if ��
if [ ���件 ]
then
.
.
fi

我�可以很�易的看到 , if � fi 之�就是典型的 if ��
假如���件成立 , 就作 then � fi之�所�的事情 .
�有其它��多多的流程控制都要用到 "���件" , 在��
介��些流程控制之前 , 我�得先介����件 .
(4.2) ���件
���件大�上可分� (I) 字串��  (II) 整���
(III) �案��
(I) 字串�� :
string1 = string2       #  string1 等於 string2
string1 != string2      #  string1 不等於 string2
string                  #  string is not null
-n string               #  string is not null
-z string               #  string is null

前面提� , ���定可能像���子 :  name=veronica
那假如我�要把 name �某一字串做相等比� , ����做 :
"$name" = veronica

假如要作不相等比� , ����做 :
"$name" != veronica

看下面的 Shell Script :
#!/bin/sh
name=veronica
if [ "$name" = veronica ]
then
echo 'equal'
fi
if [ "$name" != veronica ]
then
echo 'not equal'
fi

�行�果 :
% strcmp
equal
分析上面的 Shell Script , 首先 , 把 veronica 指定� name
此� $name 就是 veronica . �行到 if [ "$name" = veronica ]
"$name" 就��成 veronica , 所以�成 :
if [ veronica = veronica ]     = int2   大於等於
int1 -gt int2            #  int1 > int2    大於
int1 -le int2            #  int1 &-
then
echo $?
fi
if w | grep "^veronica" > /dev/null
then
echo 'veronica is online'
fi
�� Shell Script 值得好好研究 , 首先 , $? 代表上�命令的
�回值 . 假如在��系�� jhhsu 正在�上 , 而 veronica �
不在�上 . 那�次 echo $? 的�果��是 0 � 1 , 分�代表
成功�不成功 . �有 , 假如 if 所��的是命令 , 那�不需要
[  ] � . 前面�了方便 , 以及看起�有��化 , 我把 if 的��
�成 :
if [ ���件 ]
then
.
.
fi

但��上 , 原�的����是���子 :
if test_command
then
.
.
fi

也就是� , if 後接的必�是一���命令 , 而不是 [ ���件 ]
所以 , 我�得用 test ��命令��行我�的���件 . 就像下面

if test "$1" -gt 10

但是 , test ���件 也可直接�成 [ ���件 ] , 所以我就�
成 if [ ���件 ]
下面左右��的 Shell Script 是完全相等的 :
|
#!/bin/sh                     |   #!/bin/sh
|
if [ "$#" -ne 2 ]             |   if test "$#" -ne 2
then                          |   then
echo 'needs two arguments'  |     echo 'needs two arguments'
exit                        |     exit
fi                            |   fi
|
echo `expr $1 + $2`           |   echo `expr $1 + $2`
|
>&- � /dev/null 可�考 (2.5.2) , grep 可�考附�二
(4.4) �合���件的���算子
各位在看完 (4.3) 後 , 若你原本�管� , �向等等的��有一定
的�� , 那你已�可以�出��有用的 Shell Script 了 .
在 (4.4) , 我��要介�三����算子 not , and � or
(I) ! 代表反面的�� : not
也�你已���了 , 在 (4.2) 中 , 不管是字串的��或者是整�
的�� , 都有反面的��存在 :
string1 = string2         # 字串1 �字串2 相等
string1 != string2        # 字串1 �字串2 不相等
int1 -gt int2             # 整�1 大於 整�2
int1 -le int2             # 整�1 小於等於 整�2
但在�案的��方面 , ��有反面的�� . 此� , 我�就要靠
! ��成反面的�� , 如 :
if [ -f /etc/passwd ]     # /etc/passwd 是普通�案� ?
if [ ! -f /etc/passwd ]   # /etc/passwd 不是普通�案� ?
�然 , 在字串或整�比�方面 , 你也可以不用原�就已�有的�
算子 , 而改用 ! 以代表反面的�� . �看下面的 Shell Script
#!/bin/sh
if [ ! "$1" -gt 10 ]      # 假如第一���的值不大於 10
then
echo 'argument is less than or equal to 10'
fi

�行�果 :
% unary 3
argument is less than or equal to 10
% unary 11

(II) -a 代表��上的 and
�到目前�止 , 我�的比�都�限於一�表示式 , 像某一��的
�容是否等於一�字串 , 或是某一��的�容是否大於一�整�
那假如我�想做如此的�� : " 一��字是否大於 10 而且小於
100 , 也就是介於 10 到 100 之� " 那我�就得用 -a ��成 .
�看下面的 Shell Script :

#!/bin/sh
if [ "$1" -gt 10 -a "$1" -lt 100 ]  # 10 '
exit
fi
case "$1" in
0) echo 'zero';;
1) echo 'one';;
2) echo 'two';;
3) echo 'three';;
4) echo 'four';;
5) echo 'five';;
6) echo 'six';;
7) echo 'seven';;
8) echo 'eight';;
9) echo 'nine';;
*) echo 'must a single digit';;
esac
�行�果 :
% transnum 4
four
% transnum 18
must a single digit
% transnum veronica
must a single digit
在上面的 Shell Script 中 , value 是第一��� , 而 pattern
的值分�� 0,1,2,3,4,5,6,7,8,9,* . 所以 , ��行到 case �
���� , 第一���就�下面一�� pattern 做比� , 直到
遇�符合的 pattern , 就做 pattern 後面的事 . 比�特殊的是 ,
pattern 若是 * � , �表示都不符合所要做的事 . 像上面 , 我
�只接受一位�字 , 假如你��的是�位�字以上 , 或是字串 ;
既然都不符合 0 到 9 的 pattern , 那就��示一段���息 .
�有 , pattern 可以比��� , 如下面的格式都是可以的 :
[a-z]          /dev/null
do
sleep 60
done
echo "$1 has logged on"
上面是一���有用的 Shell Script , 我相信各位可以看得懂前�列
特�要解�的是 until 的�� . 在�� Shell Script 中 , 我�
想做的事是 : 每 60 秒�查一次 , 看看某 user 是不是在系�中 .
until who | grep "^$1" > /dev/null
上面�列看起�很�� , 其�只要你熟悉命令及�向,管� , 那�在
�有什�神秘的 . 我�知道 , until test_command 是只要
test_command 不成立 , 就一直做 do � done 之�的事 .
who | grep "^$1" > /dev/null
把 who 的�出交� grep 去抓出看看有�有第一���的人名 , �
把��正常�出的�息�� /dev/null ��垃圾筒 .
假如�有第一���的人名 , 就�行 sleep 60  ( sleep 60 是�
停 60 秒的意思 ) , 假如有第一���的人名 , 就��� until
�� , �且�示那�人已��入系� .
我�可以把�� Shell Script 以背景�行 , 你���它真的有用 .
(4.9) for ��
for var in word1 word2 ..... wordn
do
.
.
done
�各位注意 , Shell Programming 中的 for �一般程式�言的 for
有很大的不同 . 我�能指定的包含 var , word1 , word2 ......
( word1 word2 .... wordn �了方便 , 我把它�� word list )
以及 do � done 之�所做的事 . 我��是先看一�例子 :
#!/bin/sh
for i in 1 2 peter bob
do
echo "$i"
done
�行�果 :
% fortest
1
2
peter
bob
我�所看到的是 , for 每�行一次 , 就把後面的�料指定� var
像第一次�行� , 1 被指定� i , 第二次�行� , 2 被指定� i
第三次�行� , peter 被指定� i , 第四次�行� , bob 被指定
� i . 好了 , �在我�把焦�放在 word1 word2 ..... wordn 上
它�是不是有什��化呢 ? 在�� , 我要提出�� :
(I) 使用命令�作出 word list
各位��得 (3.5.3) 中 mail �很多人的例子� ?
mail `cat member`  '
read number1
echo -n 'Input number2 --> '
read number2
echo `expr $number1 + $number2`

�行�果 :
% plus3
Input number1 --> 5
Input number2 --> 10
15
上面�� Shell Script 可以由使用者任意�入���� , read
分�把它�放到 number1 及 number2 , 最後再把 number1 及
number2 加起� .
我再�一�例子 , 你可以用 read ��做出�似��的�西 :

#!/bin/sh
cat  '
read choice
echo -n 'Input number1 -->'
read number1
echo -n 'Input number2 -->'
read number2
case $choice in
1) echo `expr $number1 + $number2`;;
2) echo `expr $number1 - $number2`;;
3) echo `expr $number1 \* $number2`;;
4) echo `expr $number1 / $number2`;;
esac

上面是一���式的�算� , 它可以��你要作的�算 , ��取
���字���算元 . �� Shell Script 融合了我�在 (2.5.2)
中的 cat  '
read choice
echo -n 'Input number1 -->'
read number1
echo -n 'Input number2 -->'
read number2
case $choice in
1)plus;;
2)minus;;
3)multi;;
4)div;;
*)echo 'Invalid choice'
exit;;
esac
}
main_select                       # 程式����始

使用��函��作有��要注意 :
(I) 下面的情形 , 左�可以正�的�行 , 右��不行
a_func()                |      a_func
{                       |
b_func                |      a_func()
}                       |      {
|        b_func
b_func()                |      }
{                       |
|      b_func()
}                       |      {
|
a_func                  |      }
(II) 命令列下��的��必�先把�些��指定�另一���
然後才能正�的工作 . 看下面的例子 :
#!/bin/sh
main()
{
echo $1                # error ?
echo $2                # error ?
}
main
上面的例子看起��� , 但��上�不能 work , 你可能要
改成下面的�子 :
#!/bin/sh
main()
{
echo $number1
echo $number2
}
number1=$1
number2=$2
main

(5.3) Shell Script 的��
使用 sh -x shell_script_name  可以�一� Shell Script 做
��的�作 . 假如你的 Shell Script 不能正常的�行 , 或者是
�行的�果不符合你所�料的 . 出�的地方可能�下列��之一 :
(I) ��查 Shell Script 是否有可�及可�行�限 .
(II) �注意 , Shell Programming Language 不是完全自由�法
就像 if [ "$#" -ne 1 ]  就不能�成 if ["$#" -ne 1]
也不能�成 if[ "$#" -ne 1 ] , �些要特�注意 . 假如
�行�有���息 , 你要�加�查程式的�法�� . �
空格的地方要有空格 .
(III) 要了解 , $variable 才是��的�容 , 你很有可能把
variable �成��的�容 .
(IV) �有 , 你可能拼�字了 , �是很常�的 .
(V) 有些情�下 , ��中的�容�不是你�期的 , 以致造成
�� . 此�你可以在程式中可疑的地方��的加上一些
��� , 把�些���容�示出�看看 .
(V) 最後 , ��上的��是很�找到的 , 你可能也要注意 .
附�一 : Shell Summary
-------------------------------------------------------------------
## ��的指定 :
variable=value
此� , variable 是被�成字串看待 .
如 : NNTPSERVER=news.csie.nctu.edu.tw
其中 , NNTPSERVER 是 variable ; news.csie.nctu.edu.tw 是 value
## ��的�容 :
$variable
## � shell script 作���行的�作 :
sh -x [your_shell_script]
如 : sh -x search_string
其中 , search_string �� shell script
必需有 read 及 execute 的�限
## shell script 中的特殊符� :
$# ����
$n 第 n ���
$* 所有的��
$@ � $* 一� , 除了每���都加上 "  "
$? 上一�命令�回的值
$$ 目前此 shell 的 pid   // �你在 script 中要 creat 一�� , 但不希望
// ���案名是固定� ( 尤其是有��人同�使用
// �� shell script ) �不能 creat 的�名取成
// 一� , ��候 , $$ 就可派上用�
## �整�作比�的�算子 :
int1 -eq int2            //    int1 = int2
int1 -ge int2            //    int1 >= int2
int1 -gt int2            //    int1 > int2
int1 -le int2            //    int1 '
exit 1
fi
find . -inum "$1" -ok rm '{}' \;
---------------------- 字串比�的例子 -----------------------
#!/bin/sh
if [ "$#" -lt 1 ]
then
echo 'again!'
exit 1
fi
if [ "$1" = 'lala' ]
then
echo 'lala is haha'
else
echo 'unknown!!!'
fi
�行�果 :
%para3
again!
%para3 lala
lala is haha
%para3 dada
unknown!!!
--------------- �算某一目�底下有多少�案或目� ------------
#!/bin/sh
if [ "$#" -ne 2 ]
then
echo 'Usage : count_report  '
exit
fi
case "$2" in
f)
file=0
for f in `find $1 -depth 1 -type f`
do
file=`expr ${file} + 1`
done
echo "Total files is ${file}"
;;
d)
directory=0
for f in `find $1 -depth 1 -type d`
do
directory=`expr ${directory} + 1`
done
directory=`expr ${directory} - 1`
echo "Total directories is ${directory}"
;;
esac
---------- 一�比��的例子 ( 可�行但功能�不完整 :p ) -----------
#!/bin/sh
COMPILER=cc
pause_until_enter()
{
echo
echo 'Press enter to continue ......'
read enter_key
}
exit_shell_script()
{
sleep 1
clear
exit
}
error_handle()
{
if [ $? -ne 0 ]
then
echo "***** Some error occur , Please check ~/$$.err *****"
echo -n "Display ~/$$.err now ? (y/n) --> "
read error_select
if [ $error_select = 'y' ]
then
cat ~/$$.err | more
pause_until_enter
case $main_select in
1) modify_now;;
esac
fi
fi
}
modify_now()
{
echo -n 'Modify your source code now ? (y/n) --> '
read modify_now
if [ $modify_now = 'y' ]
then
$EDITOR $source_code_name
fi
}
read_source_code_name()
{
echo -n 'Enter source codes name --> '
read source_code_name
}
read_out_exe_file_name()
{
echo -n 'Input executable filename , Press enter for a.out --> '
read out_exe_file_name

if [ "$out_exe_file_name" = ''  ]
then
echo 'Use default name : a.out'
fi
}
read_library_name()
{
echo -n 'Enter your library name --> '
read library_name
}
read_obj_name()
{
echo -n 'Enter your obj name --> '
read obj_name
}
build_exe()
{
read_source_code_name
read_out_exe_file_name
echo -n 'Link library? (y/n) --> '
read link_lib_choice

if [ "$link_lib_choice" = 'y' ]
then
echo -n 'Enter library name --> '
read lib_name
else
lib_name=
fi

echo -n 'Creat symbol table for debugger? (y/n) --> '
read debug_choice
if [ "$debug_choice" = 'y' ]
then
$COMPILER -g -o ${outfile}.out $source_code_name $lib_name > ~/$$.err 2>&1
error_handle
else
$COMPILER -o ${outfile}.out $source_code_name $lib_name > ~/$$.err 2>&1
error_handle
fi
}
compile_only()
{
read_source_code_name
$COMPILER -c $source_code_name
}
list_lib_content()
{
read_library_name
ar t $library_name | more
pause_until_enter
}
add_obj_into_lib()
{
read_library_name
read_obj_name
if [ -f $library_name ]
then
ar q $library_name $obj_name
fi
}
read_config()
{
sed -n 1,1p comenv.conf
pause_until_enter
}
main_menu()
{
clear
cat  '
read main_select
case $main_select in
0);;
1) compile_menu;;
2) maintain_menu;;
3) execute_menu;;
4) config_menu;;
5) exit_shell_script;;
*) main_menu;;
esac
}
compile_menu()
{
clear
cat  '
read compile_select
case $compile_select in
1) build_exe;;
2) compile_only;;
3) main_menu;;
4) exit_shell_script;;
*) compile_menu;;
esac
}
maintain_menu()
{
clear
cat  '
read maintain_select
case $maintain_select in
1) list_lib_content;;
2) add_obj_into_lib;;
3) remove_obj_from_lib;;
4) main_menu;;
5) exit_shell_script;;
esac
}
execute_menu()
{
echo -n 'Enter execute file name --> '
read execute_file_name
$execute_file_name
pause_until_enter
}
config_menu()
{
clear
cat  '
read config_select
case $config_select in
1) read_config;;
2);;
esac
}
while test 0
do
main_menu
done
附�二 : grep �介
-------------------------------------------------------------------
grep -- ��案每一列中找出特定的�板格式
grep [options] regular_expressions [files]
options :
-b  印出符合的那一列是在整��案中的第�� byte .

-c  只印出共有多少列符合 regular_expressions .
-h  �我���多�案中找��合的�件� , 假如找到了 , 就�印出是在
那��案中找到的 . 然而 , 假如我�加了 -h �� option . 那� ,
�出的�果就不�告�我� , 是在那��案中找到符合的�板 .
-i  忽略 regular_expressions 中大小�的差� .

-l  只印出在那��案中有找到 , 而不印出�案中符合的�版 .
-n  印出符合的�板是在�案中的第�列 .
-s  抑制���息 , 像不存在的�案啦 , 或者是不能�的�案 .
-v  印出不符合 regular_expressions 的那些列 .
example :
� test1 ���案中 , 找出含有 app 的列 :

% grep 'app' test1
� passwd ���案中找出到底有多少人使用 tcsh :
% grep -c '/bin/tcsh' /etc/passwd
�目前的目�下 , 找出任一列��具有 #include 的�案 :
% grep -l '^#include' *
� test2 ���案中 , 找出那些不含 class 的列 :
% grep -v 'class' test2
列出不含 pattern 的�案 :
% grep -c pattern files | grep :0 | cut -d":" -f1
regular expressions  正�表示式
-------------------------------------------------------------

. 可符合任意一�字元 ( 包含空白字元 )
[] 可符合方括弧中列�的字元
[^ regular_expressions] 不符合 regular_expression
* �零�或更多� * 前的字元吻合
.* 符合零或更多�字元
^regular_expressions  符合在每一列��的 regular_expressions
下面是 regular expressions 的一些例子
regular expressions     match
---------------------------------------------------------------
ring                    ring , spring , ringing , stringing
^^^^     ^^^^   ^^^^        ^^^^

.alk                    walk , talk ,  alk , skialk
^^^^   ^^^^   ^^^^     ^^^^
[bB]ill                  bill , Bill , biller
^^^^   ^^^^   ^^^^
number[5-9]              number7 , number90
^^^^^^^   ^^^^^^^
[^ a-z]                  a21 , c1 , 123
^^    ^   ^^^

ab*                      acc , ab , abb , abc
^^^   ^^   ^^^   ^^^
ab.*                     ab , abb , abbc
^^   ^^^   ^^^^
^T                       符合以 T ���的每一列 ; This line
^
�考�料 :
----------------------------------------------------------------
Shell :
1.  Title: Unix Shell Programming
Authors: Stephen Kochan and Patrick Wood
Publisher: Hayden
Edition: 1990
ISBN: 0-672-48448-X
2.  Title: The Unix C Shell Field Guide
Authors: Gail Anderson and Paul Anderson
Publisher: Prentice Hall
Edition: 1986
ISBN: 0-13-937468-X
General Unix Texts :
1.  Title: Unix Power Tools
Authors: Jerry Peek, Tim O'Reilly and Mike Loukides (and other
Publisher: O'Reilly / Bantam                       contributors)
Edition: 1993
ISBN: 0-553-35402-7
2.  Title: Unix in a Nutshell
Authors: Daniel Gilly and O'Reilly staff
Publisher: O'Reilly
Edition: 2nd ed. 1992 (for System V and Solaris 2)
ISBN: 1-56592-001-5
Programming :
1.  Title: Advanced Programming in The Unix Environment
Author: Richard Stevens
Publisher: Addison-Wesley
Edition: 1992
ISBN: 0-201-56317-7
Other :
1. UNIX FAQ
如何��作者 :
地址 : 交通大�十舍315R
E-mail : jhhsu@csie.nctu.edu.tw
u8217017@cc.nctu.edu.tw
假如您��文章中有任何��之� , �通知作者 .
�有 , �者的建��批�也非常的�迎 .


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/12857/showart_230865.html
免责声明:
1、本文系本网编辑转载,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。
2、如涉及作品内容、版权和其它问题,请在30日内与本网联系,我们将在第一时间删除内容