查看完整版本: shell programming

quickleader 2004-5-26 22:37

shell programming

Shell

当我们登入 Linux 之後, 第一个接触到的, 便是 Shell. 我们必须对它有点初步的认识才行.

一. Shell 简介.

Linux 系统分成三个重要的部份

核心  
Shell  
工具程式  
核心的部份相当低阶, 操作者不易和它直接沟通, 因此, 必须要有一个友善的介面(interface), 使得操作时能更为方便, 这个介面便是 Shell.

换言之, Shell 就是一个居於核心和操作者之间的一层使用者介面.  

那麽, 为何称它为 Shell 呢? Shell 的本意是 "壳" 的意思呢!

没错, 在核心的外面, 包覆着一层外壳, 用来负责接收使用者输入的指令, 然後将指令解译成核心能够了解的方式, 传给核心去执行, 再将结果传回至预设的输出周边.

如图所示:



例如: 键入

ls -l  

shell 给你以下回应:

            拥                        
档          有                                
案          档                              
种          案                   档案大小 最近修改的
类 档案权限 数 拥有人   组别    (byte单位)日期及时间   档案名称
drwxr-xr-x  14 root     root         1024 Jul 21 21:31 .
drwxr-xr-x  17 root     root         1024 Apr 11 12:01 ..
drwxr-xr-x   8 82       82           1024 Feb 25 19:23 apache
-rw-r--r--   1 root     root      1335460 Feb 25 17:45 apache_1_3_4.tar.gz
drwxr-xr-x   6 root     root         1024 Aug 31  1998 ftp
drwxr-xr-x   5 root     root         1024 Aug 31  1998 httpd
drwx------   2 james    james        1024 Apr 23 06:47 james
drwxr-xr-x   2 root     root        12288 Aug 31  1998 lost+found
drwxr-xr-x   2 msql     nobody       1024 Dec 28  1998 msql
drwxr-xr-x  28 ols3     ols3         2048 Jul 19 21:38 ols3
drwxr-xr-x   3 1022     nobody       1024 Jan 25 23:40 ols3cgi
drwxrwxr-x   3 perl     ols3         1024 Feb  2 07:11 perl
-rw-------   1 root     root      2097152 Jul 21 18:39 quota.group
-rw-------   1 root     root      1278656 Jul 21 18:39 quota.user
drwxrwxr-x   2 root     nobody       1024 May 11  1998 samba
drwxr-xr-x   3 apache   nobody       1024 Feb 25 16:58 temp
drwxrwxr-x   2 webadm   nobody       1024 Mar 14 16:34 webadm

  

其实不只是 Linux 有这一层 Shell, 其它作业系统也有.  
比如 DOS 的 command.com, Windows 的 GUI(Graphical User Interface), Mac 的 GUI.

Shell 按着表现的方式与读取使用者输入种类的不同, 可分为二大类:

Text base : 文字导向  
Graph base: 图型导向  
所谓 "读取使用者输入种类不同" 是指: 读取自键盘, 或读取自滑鼠, 其它 serial input, 萤幕触控等.

这样说来, Shell 好像只是命令直译器罢了?!  
嗯, 这倒要按不同的 OS 所附给的 Shell 其功能和选择性的自由度而定.

以 DOS 的 COMMAND.COM 而言, 它就是一个十足的命令直译器, 除了一点点 batch 档的能力之外, 它的功能并不多. Win 平台的 GUI, 则是一个图型式的命令直译器, 介面十分友善. 不过, 这二种 OS, 不能让你自由而简单地选择 Shell.(以前 DOS 有 4dos 可选用)

Linux 的 shell, 除了做为命令直译器之外, 它也是一个不错的程式语言, 是系统管理维护时的重要工具.  

由於 Unix 家族, 对 Shell 的处理, 采独立自由开放的方式, 因此, Shell 的种类相当地多, 更可以让人自由地更换(chsh).

目前流行的 shell 有:

Bourne shell : sh  
C shell : csh  
Korn shell: ksh (商业软体)  
tcsh (free)  
Bourne Again shell: bash (GNU)  
Linux 的标准 shell 是采用 bash. 它也是我们要学习的主要对象.

二. Shell 的简史

第一个重要的 shell 是 Bourne shell (如此命名是为了纪念此 shell 的发明者 Steven Bourne), 1979 年第一个流行的 Unix 版本 7 发行时, 开始使用 Bourne shell.  

Bourne shell 的主档名为 sh, 因此, 日後人们便以 sh 为 Bourne shell 的主要识别名称.

虽然 Unix 上的 shell 有许多种, 但 Bourne shell 的地位至今仍然没有改变. 许多 Unix 系统中仍然使用 sh 做为重要的管理工具. (它的工作从开机到关机, 几乎无所不包)

第一个广为流行使用的 shell 变种是 C shell. C shell 主要附在 BSD 版的 Unix 系统中. 它的作者是柏克莱大学的 Bill Joy. C shell 主要是因为其语法和 C 语言相类似, 因而得名. 这使得 Unix 系统的程式师, 在学习 C shell 时, 感到相当地方便容易.

以上这二种形成 shell 的二大主流, 後来的变种 shell 大都攫取这二种 shell 的优点.

比如 Korn, tcsh 及 bash.

Bash shell 是 GNU 计划的重要工具软体之一, 也是 GNU 作业系统中标准的 shell.

Bash 相容於 sh, 因此, 许多早期开发出来的 Bourne shell 都可以继续在 bash 中运作. 现在我们安装好的 RedHat Linux 便是完全使用 Bash. (/bin/sh -> /bin/bash)

Bash 在 1988 年诞生, 最初的作者是 Brian Fox, Chet Ramey 於 *** 加入, 现在官方正式的维护者是 Chet Ramey, 他的工作便是持续不断地增强 bash 的功能.

1995~1996 期间推出 bash 2.0 , 在这之前, 广为使用的版本是 1.14.x, 它增加了许多新的功能, 以及更好的相容性.

当然, Bash 是完全免费的, 它是 Open Source 的一员, 原始码全部开放.

二. Bash 的功能.

Bash 具有以下功能:

相容於 Bourne shell (sh)  
包含有 C shell 以及 Korn shell 中最好的功能.  
具命令列编修的能力(您记得以前 DOS 中的 doskey 吗?)  
工作控制(job control)的能力, 可控制前景及背景程式  
具 shell 程式设计的能力, 可让您自订shell及设计程式, 管理系统.  
三. 新版的 Bash 哪里抓取?

若欲抓取新版的 bash, 可至 <!-- CETagParser ~url
<a href="http://www.gnu.org" target=_blank>http://www.gnu.org<!-- CETagParser ~/url
</a> 或其 mirror 站台.

中研院 FTP 也是不错的选择. [url=ftp://ftp.sinica.edu.tw[/url] 或 [url]ftp://linux.sinica.edu.tw]ftp://ftp.sinica.edu.tw[/......ftp://linux.sinica.edu.tw[/url]

三. 开始使用 Bash

当你 login 进 Linux 主机时, 便开始和 bash 互动, 一直到你 logout 主机(下exit,logout,或按^D) 为止.

Bash 的提示符号为 $ (代表一般身份使用者), 当您具有 root 权限时, 提示符号则变为 #.

一旦出现提示符号时, 您便可以开始键入操作命令列(command line)了.

命令可分为二大类:

bash 内建的指令  
程式  
如果是 bash 内建的指令, 则由 bash shell 负责回应; 若是程式, 则 shell 会找出该程式, 然後将控制权交给核心, 由核心执行该程式, 执行完之後, 再将控制权交回给 shell.

怎麽知道那些指令是 bash 内建的, 那些是程式呢? 通常用 "which 指令", 若没有任何回应, 表示是内建的指令(除非该指令错误、不存在, 或该程式不在预设的搜寻路径之内), 例如下:

[ols3@ols3 /ols3]$ which echo    [没有回应, 表示是内建的指令]

[ols3@ols3 /ols3]$ which ls

/bin/ls

四. 命令列的格式.

命令列通常由好几个字串组成, 中间用空白或 tab 键分开. 如下所示:

command options  arguments(或称为 parameters)  
命令 选项 参数  
rm -rf /home/ols3  

除了空白和 tab 键之外, 每一部份, 我们称之为 token, 比如上面的例子中, 便有三个 token: rm, -rf, /home/ols3.

当键入此一命令列时, shell 首先将它分解成个别的 token, 然後判断是内建的指令, 或是程式, 再按之前提过的方式去执行.

怎麽知道一个命令或程式, 它有那些选项和参数呢? 通常 man 一下该指令, 就可以得到了. 例如:

man rm

另外, 多行指令也可以一下全部写在同一命令列中, 只要中间用 ; 分开, 如:

ls ; mkdir test ; clear

五. 现行目录和自家目录.

所谓现行目录(current directory)是指: 你现在所处的位置, 又称为工作目录(working directory).

欲知现行目录为何? 可下 pwd 指令便知.

所谓自家目录(home directory)是指: 当初 root 为你建立帐号时, 所指定给你的一个私人专用的目录, 也是你登入系统之後, 第一个进入的地方.

欲知自家目录, 可用下列方式:

cd    (然後直接按 Enter)  
cd ~    (~ 代表自家目录)  
cat /etc/passwd | grep 您的帐号  
相关的技巧

cd ./myway (进入目前目录下的 myway 目录中)   
cd .. (回到上一层目录)  
cd - (回到先前的目录)  
六. 万用字元.

如果命令列的参数中, 含有档名, 那麽万用字元(wildcards)可以带来十分便利的操作. (不过若使用不当, 也是恶梦的开始)

如果各位以前有过 DOS 的操作经验, 应该还记得 * 及 ? 所代表的意义吧?!  

以下是 bash 中使用的万用字元:

? 代表任何单一字元(character)  
* 代表任何字串 (注意: 0 个以上的字元, 例: *yes 将包含 yes 或 yes-or-not)  
[字元组合] 在中括号中的字元皆符合, 如: [a-z]代表所有的小写字母  
[!字元组合] 不在中括号中的字元皆符合, 如: [!0-9]代表非数字的字元皆符合  

  

七. 输入和输出与重新导向.

当 Linux 系统完成开机之後, 预设上, 便开有三个档案, 这三个档案是做为输入、输出以及显示错误之用的.  

我们称之为:

标准输入: 通常是键盘, 档案代码为 0  
标准输出: 通常是萤幕, 档案代码为 1  
标准错误: 通常标准输出相同(也就是萤幕), 档案代码为 2  
虽然系统已帮你设好了这三个档案, 但我们仍然可以视需要, 适时地改变输入,输出,及错误这三者至不同的地方. 这种改变标准输出入的动作, 我们称之为 "I/O 重新导向" (I/O Redirection).

例如:

ls -la > myfile 就是将查询的结果重新导向至 myfile 中(本来是应该出现在萤幕上的)

cat myfile 便可以看见 ls -la 的结果.

cat < myfile > youfile 就是将 myfile 的内容拷贝给 youfile.

> 代表将输出转向

< 代表将输入转向

另一个会将输出入转向的机制是 "管线" (Pipelines).

所谓的管线就是将一个程式的输出当成另一个程式的输入.

例如: cat /etc/passwd | grep ols3

上面这段指令的意思是说: 把 /etc/passwd 档的内容显示结果(即输出) 丢给 grep 这个指令当作输入值, 然後由 grep 从中找出包含关键字 ols3 的资料列.

  

八. 前景与背景工作.

Linux 是多人多工的作业系统, 这意谓 Linux 可以让多人同时使用, 更可以同时执行许多程式.

一般而言, 你所执行的指令会一直握着控制权, 一直到程式结束为止, 我们称为这样的执行工作是在前景工作(foreground jobs), 如果, 执行指令时, 你仍然可以再做其它的事情, 那我们就称它是在背景工作.

通常比较耗时间的工作, 我们会把它丢到背景去执行, 而这期间, 我们仍然可以和 shell 继续沟通, 下达其它命令给 shell 去执行.

例如: 我们想从中研院的 FTP 伺服器下载某一个目录中所有的档案, 但又不想等它执行完毕(因为这样耗时间,也很无聊), 可以用以下的方式来达成:

ncftp -R <!-- CETagParser ~url
<a href="ftp://linux.sinica.edu.tw/pub1/redhat/powertools" target=_blank>ftp://linux.sinica.edu.tw/pub1/redhat/powertools<!-- CETagParser ~/url
</a> &

其中 & 这个符号便是将命令列丢到背景去执行的指令.

如果您想离线之後, 仍然令系统继续传档, 可以在前面再加一个 nohup 的指令, 如下:

nohup ncftp -R <!-- CETagParser ~url
<a href="ftp://linux.sinica.edu.tw/pub1/redhat/powertools" target=_blank>ftp://linux.sinica.edu.tw/pub1/redhat/powertools<!-- CETagParser ~/url
</a> &

nohup 是 no hangup 即不挂断之意.

八. 特殊字元及引号.

有许多字元, 对 shell 来说, 是具有特殊意义的. 详列於下:

符号 意义  
~ 自家目录  
` 命令取代  
# 解  
$ 变数取值  
& 背景工作  
* 万用字元  
( 子shell开始  
) 子shell结束  
\ 使特殊字元恢复本意  
| 管线  
[ 字元组合开始  
] 字元组合结束  
{  命令区块开始  
} 命令区块结束  
; 命令分隔号  
' 单引号(不具变数置换的功能)  
" 双引号(具置换的功能)  
< 输入转向  
> 输出转向  
/ 路径分隔号  
? 万用字元  
! 管线逻辑意义上的 NOT  

九. 常用控制组合键.

我们在操作 Linux 时, 常会使用一些组合键来控制 shell 的活动.

详列如下:

组合键 意义  
Ctrl - C 中止目前的命令  
Ctrl - \ 同上  
Ctrl - D 输入结束, 即 EOF 之意 (如使用 mail 信件结束时); 或 logout 登出 Linux  
Ctrl - Z 暂停目前的命令  
Ctrl - M 相当按 Enter  
Ctrl - S 暂停萤幕输出  
Ctrl - Q 恢复萤幕输出  
Ctrl - U 将命令列整列删除  
Ctrl - ? 删除最後一个字元, 相当於按 Del  

十. 指令练习.

指令 选项或参数 意义  
ls      
pwd      
which      
more      
less      
passwd      
man      
cat      
touch      
cd      
mkdir      
rmdir      
cp      
rm      
head      
tail      
wc      
grep      
ps

quickleader 2004-5-27 21:18

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(2)教学例
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 09:04:01)
"Hello world" Shell Script  
照传统程式教学例,这一节介绍Shell Script的"Hello World"如何撰写。  



--------------------------------------------------------------------------------

#!/bin/sh  
# Filename : hello  
echo "Hello world!"  

--------------------------------------------------------------------------------

大家应该会注意到第一行的"#!/bin/sh"。在UNIX下,所有的可执行Script,不管是那一种语言,其开头都是"#!",例如Perl是"#!/usr/bin/perl",tcl/tk是"#!/usr/bin/wish",看您要执行的Script程式位置在那里。您也可以用"#!/bin/bash"、"#!/bin/tcsh"等等,来指定使用特定的Shell。  
echo是个bash的内建指令。  



--------------------------------------------------------------------------------

接下来,执行hello这个script:  
要执行一个Script的方式有很多种。  



--------------------------------------------------------------------------------

第一种 : 将hello这个档案的权限设定为可执行。  
[foxman@foxman bash]# chmod 755 hello  
执行  
[foxman@foxman bash]# ./hello  
hello world  



--------------------------------------------------------------------------------

第二种 : 使用bash内建指令"source"或"."。  
[foxman@foxman bash]# source hello  
hello world  
或  
[foxman@foxman bash]# . hello  
hello world  



--------------------------------------------------------------------------------

第三种 : 直接使用sh/bash/tcsh指令来执行。  
[foxman@foxman bash]# sh hello  
hello world  
或  
[foxman@foxman bash]# bash hello  
hello world  



--------------------------------------------------------------------------------

Bash执行选项  

--------------------------------------------------------------------------------

-c string : 读取string来当命令。  
-i : 互动介面。  
-s : 由stdin读取命令。  
- : 取消往後选项的读取。  
-norc : 不要读~/.bashrc来执行。  
-noprofile : 不要读/etc/profile、~/.bash_profile、~/.bash_login、~/.profile等等来执行。  
-rcfile filename : 执行filename,而非~/.bashrc  
-version : 显示版本。  
-quiet : 启动时不要哩唆。  
-login : 确保bash是个login shell。  
-nobraceexpansion : 不要用curly brace expansion({ }符号展开)。  
-nolineediting : 不用readline来读取命令列。  
-posix : 改采Posix 1003.2标准。

quickleader 2004-5-28 23:51

Re:shell programming

众所皆知地,UNIX上以小工具着名,利用许多简单的小工具,来完成原本需要大量软体开发的工作,这一点特色,使得UNIX成为许多人心目中理想的系统平台。  
在众多的小工具中,Shell Script算得上是最基本、最强大、运用最广泛的一个。它运用围之广,不但从系统启动、程式编译、定期作业、上网连线,甚至安装整个Linux系统,都可以用它来完成。  

因为Shell Script是利用您平日在使用的一些指令,将之组合起来,成为一个"程式"。如果您平日某些序列的指令下得特别频繁,便可以将这些指令组合起来,成为另一个新的指令。这样,不但可以简化并加速操作速度,甚至还可以乾脆自动定期执行,大大简化系统管理工作。  



--------------------------------------------------------------------------------

Bash(GNU Bourne-Again SHell)是许多Linux平台的内定Shell,事实上,还有许多传统UNIX上用的Shell,像tcsh、csh、ash、bsh、ksh等等,Shell Script大致都类同,当您学会一种Shell以後,其它的Shell会很快就上手,大多数的时候,一个Shell Script通常可以在很多种Shell上使用。  
这里我介绍您bash的使用方法。事实上,当您"man bash"时,就可以看到bash的说明书,不过对许多人来说,这份说明书犹如"无字天书"一样难懂。这份文件,主要资料来源为"man bash",我加上一些实际日常的应用例来说明。希望这样能让那些始终不得其门而入的人们,多多少少能有点概念。

quickleader 2004-7-15 12:49

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(4)档案系统检查
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 11:00:00)
系统安全一向是大多数电脑用户关心的事,在UNIX系统中,最重视的事,即系统中有没有"木马"(Trojan horse)。不管Trojan horse如何放进来的,有一点始终会不变,即被放置木马的档案,其档案日期一定会被改变,甚至会有其它的状态改变。此外,许多状况下,系统会多出一些不知名的档案。因此,平日检查整个档案系统的状态是否有被改变,将所有状态有改变的档案,以及目前有那些程式正在执行,自动报告给系统管理员,是个避免坐上"木马"的良方。  

--------------------------------------------------------------------------------

#!/bin/sh  
# Filename : whatever_you_name_it  
DIRS="/etc /home /bin /sbin /usr/bin /usr/sbin /usr/local /var /your_directory"  
ADMIN="email@your.domain.com"  
FROM="admin@your.domain.com"  
# 写入Sendmail的标头  
echo "Subject: $HOSTNAME filesystem check" > /tmp/today.mail  
echo "From: $FROM" >> /tmp/today.mail  
echo "To: $ADMIN" >> /tmp/today.mail  
echo "This is filesystem report comes from $HOSTNAME" >> /tmp/today.mail  
# 报告目前正在执行的程式  
ps axf >> /tmp/today.mail  
# 档案系统检查  
echo "File System Check" >> /tmp/today.mail  
ls -alR $DIRS | gzip -9 > /tmp/today.gz  
zdiff /tmp/today.gz /tmp/yesterday.gz >> /tmp/today.mail  
mv -f /tmp/today.gz /tmp/yesterday.gz  
# 寄出信件  
sendmail -t < /tmp/today.mail  

然後把它放到一个不显眼的地方去,让别人找不到。  

把它加入crontab中。  

30 7 * * *  /full_check_script_path/whatever_you_name_it 1> /dev/null 2> /dev/null #上班前检查  

有些档案是固定会更动的,像/var/log/messages、/var/log/syslog、/dev/ttyX等等,不要太大惊小怪。

quickleader 2004-7-15 12:52

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(5)控制圈for
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 12:10:00)

示了几个简单的Shell Script,相信您应该对Shell Script有点概念了。现在我们开始来仔细研究一些较高等的Shell Script写作。一些进一步的说明,例如"$"、">"、"<"、">>"、"1>"、"2>"符号的使用,会在稍後解释。  

--------------------------------------------------------------------------------

for name [ in word; ] do list ; done
控制圈。  
word是一序列的字,for会将word中的个别字展开,然後设定到name上面。list是一序列的工作。如果[in word;]省略掉,那麽name将会被设定为Script後面所加的参数。  



--------------------------------------------------------------------------------

例一:  
#!/bin/sh  

for i in a b c d e f ; do  
    echo $i  
done  

它将会显示出a到f。  



--------------------------------------------------------------------------------

例二: 另一种用法,A-Z
#!/bin/sh  
WORD="a b c d e f g h i j l m n o p q r s t u v w x y z"  

for i in $WORD ; do  
  echo $i  
done  

这个Script将会显示a到z。  



--------------------------------------------------------------------------------

例三 : 修改副档名
如果您有许多的.txt档想要改名成.doc档,您不需要一个一个来。  
#!/bin/sh  

FILES=`ls /txt/*.txt`  

for txt in $FILES ; do  
  doc=`echo $txt | sed "s/.txt/.doc/"`  
  mv $txt $doc  
done  

这样可以将*.txt档修改成*.doc档。  



--------------------------------------------------------------------------------

例四 : meow
#!/bin/sh  
# Filename : meow  
for i ; do  
    cat $i  
done  

当您输入"meow file1 file2 ..."时,其作用就跟"cat file1 file2 ..."一样。  



--------------------------------------------------------------------------------

例五 : listbin  
#!/bin/sh  
# Filename : listbin  

for i in /bin/* ; do  
    echo $i  
done  

当您输入"listbin"时,其作用就跟"ls /bin/*"一样。  



--------------------------------------------------------------------------------

例六 : /etc/rc.d/rc  
拿一个实际的例来说,Red Hat的/etc/rc.d/rc的启动程式中的一个片断。  

for i in /etc/rc.d/rc$runlevel.d/S*; do  
    # Check if the script is there.  
    [ ! -f $i ] && continue  

    # Check if the subsystem is already up.  
    subsys=${ i#/etc/rc.d/rc$runlevel.d/S??}  
    [ -f /var/lock/subsys/$subsys ] || \  
    [ -f /var/lock/subsys/${ subsys}.init ] && continue  

    # Bring the subsystem up.  
     $i start  
done

quickleader 2004-7-16 16:50

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(6)流程控制case
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 13:04:01)
case word in [ pattern [ | pattern ] ... ) list ;; ] ... esac
case/esac的标准用法大致如下:  
case $arg in  
    pattern | sample) # arg in pattern or sample  
    ;;  
    pattern1) # arg in pattern1  
    ;;  
    *) #default  
    ;;  
esac  
arg是您所引入的参数,如果arg内容符合pattern项目的话,那麽便会执行pattern以下的程式码,而该段程式码则以两个分号";;"做结尾。  

可以注意到"case"及"esac"是对称的,如果记不起来的话,把"case"颠倒过来即可。  



--------------------------------------------------------------------------------

例一 : paranoia
#!/bin/sh  
case $1 in  
        start | begin)  
          echo "start something"  
        ;;  
        stop | end)  
          echo "stop something"  
        ;;  
        *)  
          echo "Ignorant"  
        ;;  
esac  

执行
[foxman@foxman bash]# chmod 755 paranoia  
[foxman@foxman bash]# ./paranoia  
Ignorant  
[foxman@foxman bash]# ./paranoia start  
start something  
[foxman@foxman bash]# ./paranoia begin  
start something  
[foxman@foxman bash]# ./paranoia stop  
stop something  
[foxman@foxman bash]# ./paranoia end  
stop something  

--------------------------------------------------------------------------------

例二 : inetpanel
许多的daemon都会附上一个管理用的Shell Script,像BIND就附上ndc,Apache就附上apachectl。这些管理程式都是用shell script来写的,以下示一个管理inetd的shell script。  
#!/bin/sh  

case $1 in  
    start | begin | commence)  
       /usr/sbin/inetd  
    ;;  
    stop | end | destroy)  
       killall inetd  
    ;;  
    restart | again)  
       killall -HUP inetd  
    ;;  
    *)  
       echo "usage: inetpanel [start | begin | commence | stop | end | destory | restart | again]"  
    ;;  
esac  



--------------------------------------------------------------------------------

例三 : 判断系统
有时候,您所写的Script可能会跨越好几种平台,如Linux、FreeBSD、Solaris等等,而各平台之间,多多少少都有不同之处,有时候需要判断目前正在那一种平台上执行。此时,我们可以利用uname来找出系统资讯。  
#!/bin/sh  

SYSTEM=`uname -s`  

case $SYSTEM in  
    Linux)  
        echo "My system is Linux"  
        echo "Do Linux stuff here..."  
    ;;  
    FreeBSD)  
        echo "My system is FreeBSD"  
        echo "Do FreeBSD stuff here..."  
    ;;  
    *)  
        echo "Unknown system : $SYSTEM"  
        echo "I don't what to do..."  
    ;;  
esac

quickleader 2004-7-16 16:52

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(7)流程控制select
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 14:08:00)
select name [ in word; ] do list ; done
select顾名思义就是在word中选择一项。与for相同,如果[in word;]省略,将会使用Script後面所加的参数。  

#!/bin/sh  
WORD="a b c"  

select i in $WORD ; do  
  case $i in  
    a)  
      echo "I am A"  
    ;;  
    b)  
      echo "I am B"  
    ;;  
    c)  
      echo "I am C"  
    ;;  
    *)  
      break;  
    ;;  
  esac  
done  

执行结果
[foxman@foxman bash]# ./select_demo  
1) a  
2) b  
3) c  
#? 1  
I am A  
1) a  
2) b  
3) c  
#? 2  
I am B  
1) a  
2) b  
3) c  
#? 3  
I am C  
1) a  
2) b  
3) c  
#? 4

quickleader 2004-7-16 16:54

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(8)返回状态Exit
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 15:00:00)
在继续下去之前,我们必须要切入另一个话题,即返回状态值 - Exit Status。因为if/while/until都迁涉到了使用Exit Status来控制程式流程的问题。  

--------------------------------------------------------------------------------

许多人都知道,在许多语言中(C/C++/Perl....),都有一个exit的函数,甚至连Bash自己都有个exit的内建命令。而exit後面所带的数字,便是返回状态值 - Exit Status。  
返回状态值可以使得程式与程式之间,利用Shell script来结合的可能性大增,利用小程式,透过Shell script,来完成很杂的工作。  

在shell中,返回值为零表示成功(True),非零值为失败(False)。  



--------------------------------------------------------------------------------

举例来说,以下这个两个小程式yes/no分别会返回0/1(成功/失败):  
/* yes.c */  
void main(void) {  exit(0); }  
/* no.c */  
void main(void) {  exit(1); }  
那麽以下这个"YES"的shell script便会显示"YES"。  
#!/bin/sh  
# YES  
if yes ; then  
    echo "YES"  
fi  
而"NO"不会显示任何东西。  
#!/bin/sh  
# NO  
if no ; then  
    echo "YES"  
fi  

--------------------------------------------------------------------------------

test express  
[ express ]  
在Shell script中,test express/[ express ]这个语法被大量地使用,它是个非常实用的指令。由於它的返回值即Exit Status,经常被运用在if/while/until的场合中。而在後面,我们也会大量运用到,在进入介绍if/while/until之前,有必要先解一下。  

其返回值为0(True)或1(False),要看表述(express)的结果为何。  

express格式  

-b file : 当档案存在并且属性是Block special(通常是/dev/xxx)时,返回True。  
-c file : 当档案存在并且属性是character special(通常是/dev/xxx)时,返回True。  
-d file : 当档案存在并且属性是目录时,返回True。  
-e file : 当档案存在时,返回True。  
-f file :  当档案存在并且是正常档案时,返回True。  
-g file :  当档案存在并且是set-group-id时,返回True。  
-k file :  当档案存在并且有"sticky" bit被设定时,返回True。  
-L file :  当档案存在并且是symbolic link时,返回True。  
-p file :  当档案存在并且是name pipe时,返回True。  
-r file :  当档案存在并且可读取时,返回True。  
-s file :  当档案存在并且档案大小大於零时,返回True。  
-S file : 当档案存在并且是socket时,返回True。  
-t fd : 当fd被开启为terminal时,返回True。  
-u file : 当档案存在并且set-user-id bit被设定时,返回True。  
-w file : 当档案存在并且可写入时,返回True。  
-x file : 当档案存在并且可执行时,返回True。  
-O file : 当档案存在并且是被执行的user id所拥有时,返回True。  
-G file : 当档案存在并且是被执行的group id所拥有时,返回True。  
file1 -nt file2 : 当file1比file2新时(根据修改时间),返回True。  
file1 -ot file2 : 当file1比file2旧时(根据修改时间),返回True。  
file1 -ef file2 : 当file1与file2有相同的device及inode number时,返回True。  
-z string : 当string的长度为零时,返回True。  
-n string : 当string的长度不为零时,返回True。  
string1 = string2 : string1与string2相等时,返回True。  
string1 != string2 : string1与string2不相等时,返回True。  
! express : express为False时,返回True。  
expr1 -a expr2 : expr1及expr2为True。  
expr1 -o expr2 : expr1或expr2其中之一为True。  
arg1 OP arg2 : OP是-eq[equal]、-ne[not-equal]、-lt[less-than]、-le[less-than-or-equal]、-gt[greater-than]、-ge[greater-than-or-equal]的其中之一。  




--------------------------------------------------------------------------------

在Bash中,当错误发生在致命信号时,bash会返回128+signal number做为返回值。如果找不到命令,将会返回127。如果命令找到了,但该命令是不可执行的,将返回126。除此以外,Bash本身会返回最後一个指令的返回值。若是执行中发生错误,将会返回一个非零的值。  
Fatal Signal : 128 + signo  
Can't not find command : 127  
Can't not execute : 126  
Shell script successfully executed : return the last command exit status  
Fatal during execution : return non-zero

quickleader 2004-7-16 16:55

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(9)流程控制if
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 16:10:00)

if list then list [ elif list then list ] ... [ else list ] fi
几种可能的写法  

--------------------------------------------------------------------------------

第一种  
if list then  
  do something here  
fi  
当list表述返回值为True(0)时,将会执行"do something here"。  

例一 : 当我们要执行一个命令或程式之前,有时候需要检查该命令是否存在,然後才执行。  
if [ -x /sbin/quotaon ] ; then  
    echo "Turning on Quota for root filesystem"  
    /sbin/quotaon /  
fi  

例二 : 当我们将某个档案做为设定档时,可先检查是否存在,然後将该档案设定值载入。  
# Filename : /etc/ppp/settings  
PHONE=1-800-COLLECT  

#!/bin/sh  
# Filename : phonebill  
if [ -f /etc/ppp/settings ] ; then  
    source /etc/ppp/settings  
    echo $PHONE  
fi  
执行  
[foxman@foxman ppp]# ./phonebill  
1-800-COLLECT  



--------------------------------------------------------------------------------

第二种  
if list then  
  do something here  
else  
  do something else here  
fi  
例三 : Hostname  
#!/bin/sh  
if [ -f /etc/HOSTNAME ] ; then  
    HOSTNAME=`cat /etc/HOSTNAME`  
else  
    HOSTNAME=localhost  
fi  



--------------------------------------------------------------------------------

第三种  
if list then  
  do something here  
elif list then  
  do another thing here  
fi  
例四 : 如果某个设定档允许有好几个位置的话,例如crontab,可利用if then elif fi来找寻。  
#!/bin/sh  

if [ -f /etc/crontab ] ; then  
    CRONTAB="/etc/crontab"  
elif [ -f /var/spool/cron/crontabs/root ] ; then  
    CRONTAB="/var/spool/cron/crontabs/root"  
elif [ -f /var/cron/tabs/root ] ; then  
    CRONTAB="/var/cron/tabs/root"  
fi  
export CRONTAB  



--------------------------------------------------------------------------------

第四种  
if list then  
  do something here  
elif list then  
  do another thing here  
else  
  do something else here  
fi  
例五 : 我们可利用uname来判断目前系统,并分别做各系统状况不同的事。  
#!/bin/sh  

SYSTEM=`uname -s`  

if [ $SYSTEM = "Linux" ] ; then  
  echo "Linux"  
elif [ $SYSTEM = "FreeBSD" ] ; then  
  echo "FreeBSD"  
elif [ $SYSTEM = "Solaris" ] ; then  
  echo "Solaris"  
else  
  echo "What?"  
fi

quickleader 2004-7-16 16:56

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(10)控制圈while/until
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 17:04:01)
while list do list done
当list为True时,该圈会不停地执行。  
例一 : 无限回圈写法  
#!/bin/sh  

while : ; do  
  echo "do something forever here"  
  sleep 5  
done  

例二 : 强迫把pppd杀掉。  
#!/bin/sh  

while [ -f /var/run/ppp0.pid ] ; do  
    killall pppd  
done  



--------------------------------------------------------------------------------

until list do list done
当list为False(non-zero)时,该圈会不停地执行。  
例一 : 等待pppd上线。  
#!/bin/sh  
until [ -f /var/run/ppp0.pid ] ; do  
    sleep 1  
done

quickleader 2004-7-16 16:57

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(11)参数与变数
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 18:08:00)
在继续下去介绍function之前,我们必须停下来介绍"参数与变数"。  

--------------------------------------------------------------------------------

参数(Parameters)是用来储存"值"的资料型态,有点像是一般语言中的变数。它可以是个名称(name)、数字(number)、或者是以下所列出来一些特殊符号(Special Parameters)。  
在shell中,变数是由name形式的参数所构成的。  



--------------------------------------------------------------------------------

在前面的许多例中,我们事实上已经看到许多参数的运用。要设定一个Parameter实际很简单:  
name=value  

例如说:  

MYHOST="foxman"  

而要使用它时,则是加个"$"符号。  

echo $MYHOST  



--------------------------------------------------------------------------------

位置参数(Positional Parameters)  

--------------------------------------------------------------------------------

所谓的位置参数便是0,1,2,3,4,5,6,7,8,9...。使用时,用$0,$1,$2...。  
位置参数是当script被载入时,後面所附加的参数。$0是本身,$1则为第一个参数,$2为第二个,依此类推。而当Positional Parameters被function所使用时,它们会被暂时取代(下一节会介绍function)。  

例如以下这个script:  
#!/bin/sh  
# Filename : position  
echo $0  
echo $1  

执行时:  
[foxman@foxman bash]# ./position abc  
./position  
abc  

当位置参数超过两位数时,有特别的方法来展开,称为Expansion。  



--------------------------------------------------------------------------------

特殊参数(Speical Parameters)  
这些符号,非常不人性,对新手来说很困扰。但上手後,会觉得方便无比,有些如果您看不懂的话,就--算了,不用浪费太多时间在上面。  



--------------------------------------------------------------------------------

* 星号  
将Positional Parameters合成一个参数,其间隔为IFS内定参数的第一个字元(见内建变数一节)。  
例:  
#!/bin/sh  
# starsig  
echo $*  

执行:  
[foxman@foxman bash]# starsig a b c d e f g  
a b c d e f g  



--------------------------------------------------------------------------------

@ at符号  
与*星号类同。不同之处在於不参照IFS。  

例:  
#!/bin/sh  
# atsig  
echo $@  

执行:  
[foxman@foxman bash]# atsig a b c d e f g  
a b c d e f g  



--------------------------------------------------------------------------------

# 井字号  
展开Positional parameters的数量。  

例:  
#!/bin/sh  
# poundsig  
echo $#  

执行  
[foxman@foxman bash]# poundsig a b c d e f g  
7  



--------------------------------------------------------------------------------

? 问号  
最近执行的foreground pipeline的状态。  



--------------------------------------------------------------------------------

- 减号  
最近执行的foreground pipeline的选项参数。  



--------------------------------------------------------------------------------

$ 钱钱钱  
本身的Process ID。  

[foxman@foxman bash]# ps ax | grep bash  
1635  p1 S    0:00 /bin/bash  

[foxman@foxman bash]# echo $$  
1635  



--------------------------------------------------------------------------------

! 惊号  
最近执行背景命令的Process ID。  



--------------------------------------------------------------------------------

0 零  
在Positional Parameters一部份已经说明过了,是执行的shell script本身。但如果是用"bash -c",则$0被设为第一个参数。  

[foxman@foxman bash]# echo $0  
/bin/bash  



--------------------------------------------------------------------------------

_ 底线符号  
显示出最後一个执行的命令。  

[foxman@foxman bash]# echo $_  
bash  



--------------------------------------------------------------------------------

内建变数(Shell Variables)  
Bash有许多内建变数,像PATH、HOME、ENV......等等。这些内建变数将在另一节中,专门一一说明。

quickleader 2004-7-16 16:59

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(12)函数function
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 19:00:00)
[ function ] name () {  list; }
function的参数是Positional Paraments。  
   
例  
#!/bin/sh  

function func() {   
  echo $1  
  echo $2  
  return 1  
}  

func "Hello" "function"  

局部变数可用local来宣告。  

函数可export,使用下一层的shell可以使用。  

函数可递,没有递层数的限制。

quickleader 2004-7-16 16:59

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(13)Bash内建指令集
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 20:10:00)
Bash内建指令集  
以下的命令,大部份都没有使用例,您可能会看不出所以然,摸不着头脑。在我加入例说明前,建议您"man bash",然後自己实际操作一次。  



--------------------------------------------------------------------------------

: [arguments]  
不做任何事,除了[arguments]一些参数展开及一些特定重导向的作业外。  

永远返回零。它的用法跟true一样。  



--------------------------------------------------------------------------------

.  filename [arguments]  
source filename [arguments]  
由filename中读取命令,并执行。  
您会在/etc/rc.d/*中发现很多  
. /xxxx  
的指令,而xxxx的permission都不是可执行的。事实上,在tcsh中,需要用  
source /xxxx  
来做同样的指令。  
注意到"."的後面是有空格的(比较一下". /"跟"./",不一样)。filename是内含指令的纯文字档即可,无须chmod 755 filename。  


filename : my_source  
DEV=lo  
IP=127.0.0.1  
NETMASK=255.0.0.0  
BROADCAST=127.255.255.255  

ifconfig $IP netmask $NETMASK broadcast $BROADCAST dev $DEV  

接下来  
. my_source  
或  
source my_source  

便可执行该script,而不需要"chmod 755 my_source"  



--------------------------------------------------------------------------------

alias [name[=value] ...]  
昵称命令  
例如您如果来自DOS的世界,对UNIX的指令不习惯,可用alias来修改,以符合您的习惯。  


alias ls="ls --color"  
alias dir="ls"  
alias cd..="cd .."  
alias copy="cp -f" # dangerous, recommend, "cp -i"  
alias del="rm -f" # dangerous, recommend, "rm -i"  
alias move="mv -f" # dangerous, recommend, "mv -i"  
alias md="mkdir"  
alias rd="rmdir"  

--------------------------------------------------------------------------------

unalias [-a] [name ...]  
unalias取消alias的设定。"unalias -a"将全部alias取消。  


unalias copy  

--------------------------------------------------------------------------------

bg [jobspec]  
将指定任务放到背景中,如果jobspec未指定,内定为目前的。  



--------------------------------------------------------------------------------

fg [jobspec]  
将指定任务放到前景中,如果jobsepc没有指定,那麽内定为目前的。  



--------------------------------------------------------------------------------

jobs [-lnp] [ jobspec ... ]  
第一种形式列出目前正在工作的任务。  
-l : 除了列出一般资讯外,还列出Process IDs。  
-p : 仅列出该工作群"首脑"(Process group leader)的Process ID.  
-n : 则仅列出有改变的jobs的状态。  
如果给定jobspec,输出资讯则只有该jobspec。  

返回值为零,除非有非法的选项发生。  

jobs -x command [ args ... ]  

如果使用第二种形式(-x),jobs取代指定的command及args,并执行返回其Exit Status。  



--------------------------------------------------------------------------------

kill [-s sigspec | -sigspec] [pid | jobspec] ...  
将sigspec的信号送到pid或jobspec。  
sigspec可以是SIGKILL/KILL这种形式或是信号号码。如果sigspec是signal name,则大小写无关,而且可以没有SIG。  
kill -l [signum]  
列出信号名称。  

[foxman@foxman bash]# kill -l  
1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL  
5) SIGTRAP      6) SIGIOT       7) SIGBUS       8) SIGFPE  
9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2  
13) SIGPIPE     14) SIGALRM     15) SIGTERM     17) SIGCHLD  
18) SIGCONT     19) SIGSTOP     20) SIGTSTP     21) SIGTTIN  
22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ  
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO  
30) SIGPWR  



--------------------------------------------------------------------------------

wait [n]  
等待指定的行程,并返回其结束状态。n可以是个jobspec或Process ID。如果n未指定,则等待所有的子行程,及返回值为零。若n为不存在的job或process,则返回127。否则,返回值为最後一个job/process的Exit Status。  



--------------------------------------------------------------------------------

bind [-m keymap] [-lvd] [-q name]  
bind [-m keymap] -f filename  
bind [-m keymap] keyseq:function-name  
显示出目前readline的按键及链结函数设定或是巨集。  

-m keymap : 设定keymap binding。  
-l : 显示出所有readline function的名称。  
-v : 显示出目前的function name及bindings。  
-d : 显示出function name及bindings。  
-f filename : 从filename读取key bindings。  
-q function : 询问那个按键触发function。  



--------------------------------------------------------------------------------

break [n]  
跳出控制回圈for/while/until中使用。如果有指定n,则跳出n层。n必须是大於等於1。若n大於巢状圈数,则所有的圈都会跳离。返回值回零。  



--------------------------------------------------------------------------------

continue [n]  
还原控制回圈for/while/until中使用。如果有指定n,则返回n层。n必须是大於等於1。若n大於巢状圈数,则还原到最上层。返回值回零。  



--------------------------------------------------------------------------------

exit [n]  
离开程式。n是Exit Status。  



--------------------------------------------------------------------------------

return [n]  
在function中使用。n为返回值,其作用与Exit Status一样。  



--------------------------------------------------------------------------------

builtin shell-builtin [arguments]  
执行内建函数。当您定义了与内建函数相同的指令时,可用此命令来执行内建函数。  



--------------------------------------------------------------------------------

cd [dir]  
更换目录到dir。如果没有指定,内定为HOME所指定的目录。  



--------------------------------------------------------------------------------

command [-pVv] command [arg ...]  
用command指定可取消正常的shell function寻找。只有内建命令及在PATH中找得到的才会被执行。"-p"选项,搜寻命令的方式是用PATH来找。"-V"或"-v"选项,会显示出该命令的一些简约描述。  



--------------------------------------------------------------------------------

declare [-frxi] [name[=value]]  
typeset [-frxi] [name[=value]]  
宣告参数并给它们设定属性。如果没有给定名称,将会显示各参数值。  

-f : 仅使用函数名称。  
-r : 将name设为readonly。  
-x : 将name输出给後续环境使用。  
-i : 该参数被设为integer来使用,可用於算术表述。  

用"+"时,关闭该属性。  



--------------------------------------------------------------------------------

dirs [-l] [+/-n]  
显示目前记忆的目录。目录可透过pushd/popd来操作。  

+n : 显示开始的记录n个。  
-n : 显示结尾的记录n个。  
-l : 显示较多的资讯。  



--------------------------------------------------------------------------------

echo [-neE] [arg ...]  
输出显示args,由空白分隔。返回值永为零。  

-n : 不跳行。  
-e : 启动"\"符号的解译。  
-E : 将ESC解译功能取消。  

"\a" : alert(bell),发出声响。  
"\b" : backspace,倒退。  
"\c" : suppress trailing newline,不跳行。  
"\f" : form feed,跳行跳格。  
"\n" : new line,新行。  
"\r" : carriage return,回到行起点。  
"\t" : horizontal tab,水平跳位。  
"\v" : vertical tab,垂直跳位。  
"\\" : 输出"\"。  
"\nnn" : 输出ASCII Code号码nnn(八进位)。  



--------------------------------------------------------------------------------

enable [-n] [-all] [name ...]  
启动或关闭内建函数命令。使用"-n"将所有指定命令皆关闭,否则都是启动的。如果只有"-n"参数,它将会显示所有关闭的函数。如果只有"-all",它将会显示所有内建命令。  



--------------------------------------------------------------------------------

eval [arg ...]  
读取args,并将args合为一个命令,然後执行。其返回值成为eval的返回值。如果没有参数,eval返回True。  



--------------------------------------------------------------------------------

exec [[-] command [arguments]]  
当命令执行时,该命令取代shell,没有新的process产生。如果第一个参数是"-",shell会将"-"放入第零个参数,传给command。  



--------------------------------------------------------------------------------

export [-nf] [name[=word]] ...  
export -p  
将name输出给环境,给往後的命令使用。"-f"选项表示name是函数。"-p"显示出所有export的名称。"-n"移除name。  



--------------------------------------------------------------------------------

set [--abefhkmnptuvxldCHP] [-o option] [arg ...]  
-a : 自动将变数标记为可让後面环境所使用。  
-b : 立即报告被终结的背景程式状态。  
-e : 当命令(simple-command,见後面)返回非零值时,立即跳出。  
-f : 取消pathname expansion。  
-h : 找出所记忆的函数命令位置。  
-k : 所有keyword参数都放到环境中。  
-m : 监督模式。  
-n : 读取命令,但不要执行。可用於语法检查。  
-p : 打开privileged模式。  
-t : 当读取一个命令并执行後,立即离开。  
-u : 当参数展开时,把unset参数当成是错误。  
-v : 列出shell input lines。  
-x : 在展开每个simple-command後,bash显示展开值在PS4上。  
-l : 储存并还原name binding在for语法中。  
-d : 关闭hasing command搜寻。  
-C : 跟`noclobber=`一样。请见内定参数一节。  
-H : 启动! style history substitution。  
-P : 在使用像cd这种指令时,不要跟随symbolic links。  
-- : "--"之後,没有参数跟在後面。  
- : 指定将所有後面的参数当成是位置参数。  
-o option-name : option-name可以是以下之一  
allexport : 与"-a"相同。  
braceexpand : 启动Brace Expansion。这是内定设定。  
emacs : 使用emacs-style命令列编辑界面。  
errexit : 与"-e"相同。  
histexpand : 与"-H"相同。  
ignoreeof : 效果跟`IGNOREEOF=10`一样。  
interactive-commands : 允许#做为解。  
monitor : 与"-m"相同。  
noclobber : 与"-C"相同。  
noexec : 与"-n"相同。  
noglob : 与"-f"相同。  
nohash : 与"-d"相同。  
notify : 与"-b"相同。  
nounset : 与"-u"相同。  
physical : 与"-P"相同。  
posix : Bash行为修改为Posix 1003.2标准。  
privileged : 与"-p"相同。  
verbose : 与"-v"相同。  
vi : 使用vi-style命令列编辑程式。  
xtrace : 与"-x"相同。  



--------------------------------------------------------------------------------

unset [-fv] [name ...]  
移除对映於name的参数。要注意PATH、IFS、PPID、PS1、PS2、UID、EUID不能unset。若RANDOM、SECONDS、LINENO、HISTCMD被unset,它们会丧失原有意义,既始它们後来被重设也一样。返回值为True,除非name是不能被unset的。  



--------------------------------------------------------------------------------

fc [-e ename] [-nlr] [first] [last]  
fc -s [pat=rep] [cmd]  
修正命令。  



--------------------------------------------------------------------------------

getopts optstring name [args]  
解析位置参数。  



--------------------------------------------------------------------------------

hash [-r] [name]  
对每个name命令的完整路径记录下来。"-r"选项强迫忘记所有命令位置。如果没有给参数,则将会印出所有的资讯。返回值为True。  



--------------------------------------------------------------------------------

help [pattern]  
显示协助资讯。  



--------------------------------------------------------------------------------

history [n]  
history -rwan [filename]  
没有参数时,会显示所下命令的历史记录。带有参数"n"则显示最後n个。  

其它参数如下:  
-a : 新增"新历史"到历史档中。  
-n : 读取尚未读到历史中的记录。  
-r : 读取filename做为历史档,并用它为目前历史记录。  
-w : 将现有历史记录写到filename中。  



--------------------------------------------------------------------------------

let arg [arg ...]  
算术表述。请参考算术表述一节。  



--------------------------------------------------------------------------------

local [name[=value] ...]  
产生一个局部参数。如果用於function,则其作用围在function内及其子函数。  



--------------------------------------------------------------------------------

logout  
离开login shell。  



--------------------------------------------------------------------------------

popd [+/-n]  
移除目录堆叠。"+n"移除上面n个,"-n"移除下面n个。  



--------------------------------------------------------------------------------

pushd [dir]  
pushd +/-n  
将目录新增到目录堆叠的最上面。"+n"旋转该堆叠,使第n个目录变成最上面。"-n"旋转该堆叠,使倒数第n个目录变成最上面。  



--------------------------------------------------------------------------------

pwd  
列出目前工作目录的绝对路径。  



--------------------------------------------------------------------------------

read [-r] [name ...]  
读进一行,然後第一个字设到第一个name,第二个设到第二个name,依此类推。如果没有name在参数中,则read会将值设到REPLY。返回值为零,除非遇到End-Of-File。若有"-r"选项,则"\n"被考虑为该行的一部份。  



--------------------------------------------------------------------------------

readonly [-f] [name ...]  
readonly -p  
将给定的name标记为readonly。如果是"-f"选项,则函数也一样被标记为readonly。"-p"会列出所有readonly的name。"--"取消检查剩馀的参数。  



--------------------------------------------------------------------------------

shift [n]  
Positional Parameters从n+1...开始,会被改为$1...。n若为零,则没有改变。n若未给定,则内定为1。n必须是非负数,并且小於或等於$#。若n大於$#,则没有改变。返回值为零,除非n大於$#或小於零。  



--------------------------------------------------------------------------------

suspend [-f]  
暂停这个shell的执行,直到它收到SIGCONT信号。"-f"选项则是叫login shell不要抱怨,不过还是一样暂停。返回状态零,除非该shell是个login shell,而且没有"-f"选项。  



--------------------------------------------------------------------------------

test expr  
[ expr ]  
我们在Exit Status的部份已经说过了,不再重。  



--------------------------------------------------------------------------------

times  
列出该shell的累积的使用者及系统时间及从shell执行的process时间,返回值为零。  



--------------------------------------------------------------------------------

trap [-l] [arg] [sigspec]  
当收到sigspec信号时,执行arg命令。"-l"显示出信号名称及号码。  



--------------------------------------------------------------------------------

type [-all] [-type | -path] name [name ...]  
没有参数的状况下,它会显示出shell如何解译name做为命令。如果有"-type",它将会显示alias、keyword、function、builtin或file。如果有"-path"的参数,它将会显示该命令的路径,找不到的话,不显示任何东西。如果有"-all"的参数,它将会显示所有可执行name的可能路径。type接受"-a"、"-t"、"-p"做为缩写。  



--------------------------------------------------------------------------------

ulimit [-SHacdfmstpnuv [limit]]  
ulimit提供了对shell的可获取资源控制的功能。  

-a : 报告目前所有限制。  
-c : 设定最大可产生的core档案。  
-d : 行程资料段(process's data segment)最大值。  
-f : 可被这个shell产生的最大档案。  
-m : resident set size最大值。  
-s : 堆叠最大值。  
-t : CPU TIME最大值(以秒计算)。  
-p : pipe size in 512-byte blocks的最大值。  
-n : 可开启的file descriptors最大值。  
-u : 单一使用者可使用的最大process数。  
-v : 该shell最大虚拟记忆体可用值。  

所有项目是以1024做为单位。

quickleader 2004-7-16 17:00

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(14)Bash内建参数
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 21:04:01)

PPID : 该bash的呼叫者process ID.  
PWD : 目前的工作目录。  

OLDPWD : 上一个工作目录。  

REPLY : 当read命令没有参数时,直接设在REPLY上。  

UID : User ID。  

EUID : Effective User ID。  

BASH : Bash的完整路径。  

BASH_VERSION : Bash版本。  

SHLVL : 每次有Bash执行时,数字加一。  

RANDOM : 每次这个参数被用到时,就会产生一个乱数在RANDOM上。  

SECONDS : 从这个Shell一开始启动後的时间。  

LINENO : Script的行数。  

HISTCMD : 历史记录数。  

OPTARG : getopts处理的最後一个选项参数。  

OPTIND : 下一个要由getopts所处理的参数号码。  

HOSTTYPE : 机器种类。  

OSTYPE : 作业系统名称。  

IFS : Internal Field Separator。  

PATH : 命令搜寻路径。  
              PATH="/usr/gnu/bin:/usr/local/bin:/usr/ucb:/bin:/usr/bin:."  

HOME : 目前使用者的home directory;  

CDPATH : cd命令的搜寻路径。  

ENV : 如果这个参数被设定,每次有shell script被执行时,将会执行它所设定的档名做为环境设定。  

MAIL : 如果这个参数被设定,而且MAILPATH没有被设定,那麽有信件进来时,bash会通知使用者。  

MAILCHECK : 设定多久时间检查邮件一次。  

MAILPATH : 一串的邮件检查路径。  

MAIL_WARNING : 如果有设定的话,邮件被读取後,将会显示讯息。  

PS1 : 提示讯息设定,内定为"bash\$ "。(请详见提示讯息一节。)  

PS2 : 第二提示讯息设定,内定为"> "。  

PS3 : select命令所使用的提示讯息。  

PS4 : 执行追踪时用的提示讯息设定,内定为"+ "。  

HISTSIZE : 命令历史记录量,内定为500。  

HISTFILE : 历史记录档,内定~/.bash_history。  

HISTFILESIZE : 历史记录档行数最大值,内定500。  

OPTERR : 如果设为1,bash会显示getopts的错误。  

PROMPT_COMMAND : 如果设定的话,该值会在每次执行命令前都显示。  

IGNOREEOF : 将EOF值当成输入,内定为10。  

TMOUT : 如果设为大於零,该值被解译为输入等待秒数。若无输入,当成没有输入。  

FCEDIT : fc命令的内定编辑器。  

FIGNORE : 请详见READLINE。  

INPUTRC : readline的startup file,内定~/.inputrc  

notify : 如果设定了,bash立即报告被终结的背景程式。  

history_control, HISTCONTROL : history使用。  

command_oriented_history : 存入多行指令。  

glob_dot_filenames : 如果设定了,bash将会把"."包含入档案路径中。  

allow_null_glob_expansion : 如果设定了,bash允许路径明称为null string。  

histchars : history使用。  

nolinks : 如果设定了,执行指令时,不会跟随symbolic links。  

hostname_completion_file, HOSTFILE : 包含与/etc/hosts相同格式的档名。  

noclobber : 如果设定了,Bash不会覆写任何由">"、">&"及"<>"所操作的档案。  

auto_resume : 请见任务控制一节。  

no_exit_on_failed_exec : 如果该值存在,非互动的shell不会因为exec失败而跳出。  

cdable_vars : 如果启动,而cd命令找不到目录,可切换到参数形态指定的目录下。

quickleader 2004-7-16 17:01

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(15)提示符号
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-25 22:08:01)

Bash使用PS1~PS4来显示提示符号,其格式如下:  



--------------------------------------------------------------------------------

\t : 现在时间。  
\d : 现在日期。  
\n : 新行。  
\s : shell的名称。  
\w : 目前工作目录。  
\W : 目前工作目录完整路径。  
\u : 使用者名称。  
\h : Hostname。  
\# : 这个命令的号码。  
\! : 历史号码。  
\$ : 如果EUID是0,则#,否则为$。  
\nnn : 八进位的字元。  
\\ : "\"符号。  
\[ : 开始一序列不可列印的字元。  
\] : 结束一序列不可列印的字元。

quickleader 2004-7-16 17:03

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(16)算术表述
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-26 07:00:00)


- +  
! ~  
* / %  
+ -  
<< >>  
<= >= < >  
== !=  
&  
^  
|  
&&  
||  
= *= /= %= += -= <<= >>= &= ^= |=

quickleader 2004-7-16 17:03

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(17)重导Redirection
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-26 08:10:00)

>  
>>  
1>  
.  
.

quickleader 2004-7-16 17:04

Re:shell programming

Linux程式设计-11.Shell Script(bash)--(18)语法
<!-- CETagParser ~url
<a href="http://www.openchess.org/noitatsko/programming/" target=_blank>http://www.openchess.org/noitatsko/programming/<!-- CETagParser ~/url
</a> (2001-05-26 09:04:00)


Simple Command  
   
   
   
   
Pipelines  
   
   
   
   

Lists  

(list)  
{  list; }

quickleader 2004-7-16 17:06

Re:shell programming

Shell 递归程序设计-目录列表
本文出自: <!-- CETagParser ~url
<a href="http://ehnt.net/clansoft/gb/" target=_blank>http://ehnt.net/clansoft/gb/<!-- CETagParser ~/url
</a> (2001-06-18 11:00:00)

quickleader 2004-7-16 17:06

Re:shell programming

设置Unix启动密码
本文出自: <!-- CETagParser ~url
<a href="http://go3.163.com/~axiom999" target=_blank>http://go3.163.com/~axiom999<!-- CETagParser ~/url
</a> (2001-06-18 22:08:01)
     在DOS和WINDOWS98系统中设置启动密码已经有很多方法和现成的软件可用了,但是,在UNIX系统中,有没有一种方法可以设置启动密码呢?下面我自编了一个SHELL小程序,可以实现在系统启动过程中加以密码限制,即使启动了机器,在输入密码之前,也不能使系统出现LOGIN的提示符。
     这个小程序是加载在/etc/rc2文件中的,rc2文件是unix系统在启动过程中调用的一个shell文件,我们在最后加入如下内容,就可实现启动密码的限制了。程序如下:


在/etc/rc2文件中寻找下面这句话

if [ "${ BOOT}" = "yes" -a "$7" = "2" ]
then
# dspmsg $MF_RC -s $MS_RC2 $RC2_READY 'The System is Ready ! \n'
找到后
屏蔽上面这句话,之后加入下面这个程序:
# 循环开始  
while true
do
clear
setcolor -b black -k lt_red  
echo '\033[09;18H 系'
setcolor -b black -k lt_cyan  
echo '\033[09;25H 统'
setcolor -b black -k lt_magenta  
echo '\033[09;32H 启'
setcolor -b black -k hi_white  
echo '\033[09;39H 动'
setcolor -b black -k yellow  
echo '\033[09;46H 完'
setcolor -b black -k lt_green  
echo '\033[09;53H 毕'
setcolor -b black -k white
echo '\n\n\n\n\n\n\n\n\n\n\n\n\n'
echo ' ┏━━━━━━━━━━━━━━━━━┓'
echo ' ┃          欢 迎 您 使 用 UNIX             ┃'  
echo ' ┗━━━━━━━━━━━━━━━━━┛'
echo "\033[17;15H 请输入密码:[****] "
setcolor -b black -k black
echo "\033[17;42H\c"
read ch
setcolor -b black -k white
# 在上面的两个setcolor语句中,可以使键入的密码不回显
case $ch in

# 以下括号内为启动密码
2015)
setcolor -b black -k lt_green
echo " "
echo " "
echo "密码正确,请使用本系统."
setcolor -b black -k white  
break
;;
*)
echo "\007"
echo "\033[20;1H"
setcolor -b black -k lt_red
echo "\007\t\t\t 密码错误!!!,请重新输入\c"
setcolor -b black -k white  
read s  
;;  

esac
done
# 循环结束  

elif [ "$7" = "2" ]
then
dspmsg $MF_RC -s $MS_RC2 $RC2_COMPLETE 'Change to state 2 has been completed.\n'
fi
如果在while循环中,每一条setcolor语句下面加如sleep 1(延迟1秒),更能获得动态
的效果!

quickleader 2004-7-16 17:07

Re:shell programming

Shell 递归程序设计-批量转换大写文件名为小写
本文出自: <!-- CETagParser ~url
<a href="http://ehnt.net/clansoft/gb/" target=_blank>http://ehnt.net/clansoft/gb/<!-- CETagParser ~/url
</a> (2001-06-19 14:08:00)

quickleader 2004-7-16 17:07

Re:shell programming

shell实现Unix进程间信息交换的几种方法
本文出自:计算机世界2000年第32期 作者: 北京中软同和公司 何绍德 (2001-06-26 22:08:00)
  本文将介绍在SCO OpenServer5.0.5系统中使用shell语言来实现进程间信息交换的几种方法:  

使用命名管道实现进程间信息交换  

使用kill命令和trap语句实现进程间信息交换  

使用点命令“.”实现进程间信息交换  

使用export语句实现父进程对子进程的信息传递  

一、使用命名管道
  命名管道是一种先进先出(FIFO)的数据结构,它允许两个进程通过管道联接实现信息交换。
在Unix系统中,命名管道是一种特殊类型的文件,因此可以对命名管道进行读写操作;当然,同样
也会有读写和执行等权限的限制。  

  通过下面的命令可以创建一个命名管道:
  /etc/mknod pipe_name p  

  其中“pipe_name”是要创建的命名管道的名字,参数p 必须出现在命名管道名字之后。  

  命名管道文件被创建后,一些进程就可以不断地将信息写入命名管道文件里,而另一些进程也
可以不断地从命名管道文件中读取信息。对命名管道文件的读写操作是可以同时进行的。下面的例子
显示命名管道的工作过程。  

  进程A、B、C中运行的程序只是一条简单的echo命令,它们不断地把信息写入到命名管道文件
/tmp/pipe1中。与此同时,程序中的“read msg” 命令不断地从命名管道文件/tmp/pipe1中读取这些
信息,从而实现这些进程间的信息交换。  

  程序执行时,首先创建命名管道文件,此时程序处于等待状态,直到A、B、C进程中某一个进程往
命名管道中写入信息时,程序才继续往下执行。使用rm命令可以删除命名管道文件从而清除已设置的
命名管道。  

  下面是一个用于记录考勤的例子:  

  在主机上运行的程序/tmp/text产生命名管道/tmp/pipe1,并不断地从命名管道中读取信息送屏幕
上显示。  

/tmp/text程序:
if [ ! -p /tmp/pipe1 ]
then
  /etc/mknode /tmp/pipe1 p
fi
while :
do
  read msg
  if [ “$msg" = “" ]
  then
continue
  else
echo “$msg"
  fi
done < /tmp/pipe1

  在终端上运行的是雇员签到程序/tmp/text1。每个雇员在任何一台终端上键入自己的名字或代码,
程序/tmp/text1将把这个名字连同当时的签到时间送入命名管道。  

/tmp/text1程序:
tty=‘who am I | awk ‘{ print $2}’’
    while :
    do
      echo “Enter your name: \c" > /dev/$tty
      read name
      today=‘date’
      echo “$name\t$today"
      done > /tmp/pipe1

  当雇员从终端上输入自己的姓名后,运行/tmp/text程序的主机将显示类似下面的结果:  

wang Thu Jan 28 09:29:26 BTJ 1999
he Thu Jan 28 09:29:26 BTJ 1999
cheng Thu Jan 28 09:30:26 BTJ 1999
zhang Thu Jan 28 09:31:26 BTJ 1999

二、使用kill命令和trap语句
  在Unix系统中,当检测到一个异常的内部状态,或者硬件及外部设备发出请求,或者执行某些指令时,
将会向系统中的进程发出信号报告事件产生。当进程捕获到这些信号后,系统便转去执行预先设定的默认
程序,完成指定的动作;这些预先设定的默认程序称之为信号的系统陷阱。  

  在shell中,使用trap语句为信号设置新的陷阱。当shell 捕获到一个信号时(信号11除外,因为
shell本身要利用这个信号进行内存分配),它将这个信号传递给所有当前正在执行的程序
(父程序和子程序),并分别执行父程序和子程序中已设置的信号陷阱。一旦陷阱程序执行结束,便返
回中断点,继续执行原来的程序流程。  

    trap语句的基本格式:
trap command_list signal_list

  command_list: 由一个或多个命令(或命令组)构成的命令列表。当命令列表中含有多个命令时要
用单引号或双引号括起来,并且各命令间要用分号隔开。  

  signal_list:由一个或多个信号值构成的信号列表,各信号值间要用空格分开。  

  在一个shell程序(父程序)中重新设置信号的陷阱并不改变被这个程序所调用的子程序中同名信号
的陷阱。同样,在子程序中设置的信号陷阱也不影响父程序中同名信号的陷阱。  

  shell在读取trap语句时,要扫描一次命令列表,以便设置陷阱。在捕获信号后,shell再次扫描命令
列表,执行已设置好的陷阱程序(命令或命令组)。因此,如果命令列表中含有变量置换或命令置换表达
式,shell在第一次扫描命令列表时就会用当前的变量值或命令结果置换这些表达式,使得在捕获到信号而
去执行陷阱程序时,陷阱程序已经不是原来设置的陷阱程序了。为了避免这种情况发生,使用单引号而不是
使用双引号把trap语句中含有变量置换或命令置换表达式的命令列表括起来;因为单引号可以消除所有字符
的特殊含义,这样避免了shell在第一次扫描时执行任何置换或替代操作,直到命令列表被执行时才进行置
换或替代。  

  向一个程序或进程传递信号方法很多,比如在程序执行时按下Ctrl+c键或Del键,将向程序传递一个
SIGINT信号,执行该信号的系统陷阱将终止程序执行。使用kill命令传递信号是shell语言编程中最为常用
的方法。  

  kill命令的基本格式是:
  kill [ - signal ] PID  

  通常kill命令用来终止一个进程。但如果使用了带有短划线“-”的信号作为参数时,kill命令就发送
该信号给PID指示的一个或多个进程,而不是终止进程。当trap语句捕获到这个信号后便执行设定的信号陷阱
程序,实现进程间的相互通讯。  

  下面的例子显示了程序master和slave1、slave2间如何利用信号机制实现相互通讯的。首先在后台运行
程序slave1和slave2,然后运行程序master。在文件/tmp/pro_list中记录了这三个程序的进程号。  

  程序slave1首先设置信号15的陷阱,然后把自己的当前进程写入文件/tmp/pro_list;在获得master进程
号后,进入循环状态。当接收到master发出的信号15时,执行陷阱程序,显示相关信息后,向master发出信
号15。  

  程序slave2执行情况与slave1相似。  

  程序master也是首先设置信号15的陷阱,然后把自己的当前进程写入文件/tmp/pro_list。在取得所有
slave程序进程号后,向这些slave程序发出信号15,然后进入循环等待。当接收到slave1或slave2发出的信
号15时,执行陷阱程序,显示相关信息,杀死所有slave进程,清空文件/tmp/pro_list,然后退出。  

    程序/tmp/slave1:
slave() {  
  echo “slave1 has received sighal from master"
  echo “Request master to kill slave1 process"
  kill -15 $master_pid
}
trap slave 15
echo “slave1_pid $$" >> /tmp/pro_list
sleep 1
while :
do
  master_pid=‘awk ’$1 ~/master/
   { print $2}‘/tmp/pro_list’
  if [ “$master_pid" != “" ]
  then break
  fi
done
while :
do
  sleep 1
done

    程序/tmp/slave2:
slave() {  
  echo “slave2 has received sighal from master"
  echo “Request master to kill slave2 process"
  kill -15 $master_pid
}
trap slave 15
echo “slave2_pid $$" >> /tmp/pro_list
sleep 1
while :
do
  master_pid=‘awk ’$1 ~/master/  
   { print $2}‘/tmp/pro_list’
  if [ “$master_pid" != “" ]
  then break
  fi
done
while :
do
  sleep 1
done

    程序/tmp/master:
kill_slave() {  
      echo “Master has received signals
            from slave processes"
      echo “End all slave processes"
      kill -9 $slave_list
>/tmp/pro_list
    exit 0
}
trap kill_slave 15
echo “master_pid $$" >> /tmp/pro_list
sleep 1
slave_list=‘awk ’$1 ~/slave/  
     { print $2}‘/tmp/pro_list’
echo “Current slave processes are:"
echo “$slave_list"
kill -15 $slave_list
while :
do
  sleep 1
done

    执行程序:
$ cd /tmp
$ ./slave1&
15638
$ ./slave2&
16831
$ ./master
Current slave processes are:
15638
16831
slave1 has received signal 15 from master
Request master to kill slave1 process
slave2 has received signal 15 from master
Request master to kill slave2 process
Master has received signals from slave processes
End all slave processes
15638 Killed
16831 Killed


三、使用点命令“.”
  “.”点命令是shell的一个内部命令,它从指定的shell 文件中读入所有命令语句并在当前进程
中执行。 因此当多个shell进程(父子进程或无关进程均可)共享一组变量值时,就可以将这些变量
赋值语句定义到一个shell文件里,并在需要这些变量值的程序中使用点语句来引用这个shell文件,
从而实现变量值共享(对这些变量值的修改仅涉及到这个shell文件)。但要注意的是,这个shell文
件不能包括含有位置参数的语句,即不能接受$1、$2等命令行参数。  

  下面是一个在超市中发布每日商品价格的示范程序片段。发布每日商品价格统一由程序/tmp/jiage
来执行,它为每种商品价格赋值,并把相应的赋值语句写入文件/tmp/jiagebiao中。在各终端上运行的
收款程序/tmp/shoukuan将读入文件 /tmp/jiagebiao中所有赋值语句并在当前进程中执行,从而获取在
程序/tmp/jiage中设定的价格。  

  价格设定程序/tmp/jiage:  

echo “Enter the price of chicken,
       duck and fish: \c"
read chicken duck fish
exec 3>/tmp/jiagebiao
echo “chicken_price=$chicken" >&3
echo “duck_price=$duck" >&3
echo “fish_price=$fish" >&3

  执行/tmp/jiage程序后,文件/tmp/jiagebiao中将有如下内容:  

chicken_price=5.4
duck_price=2.5
fish_price=4.2

  收款程序/tmp/shoukuan:  

. /tmp/jiagebiao
count=0
while :
do
echo “Enter the trade name and
      quantities or input q to sum: \c"
read trade$count quantity$count
eval a=\$trade$count
if [ “$a" = “q" ]
then if [ $count -gt 0 ]
then
count=‘expr $count - 1’
fi
break
fi
count=‘expr $count + 1 ’
done
echo “\n‘date’"
echo “trade name\tquantity\tsum"
while [ “$count" -ge 0 ]
do
      eval trade=“\${ trade$count}"
      eval trade_price=“${ trade}_price"
      eval danjia=\${ $trade_price}
      eval quantity=“\${ quantity$count}"
      sum=‘echo “scale=2; $danjia
          *$quantity"|bc’
      echo “$trade\t\t$quantity\t\t$sum"
      count=‘expr $count - 1 ’
done

  在终端上执行程序/tmp/shoukuan将有如下显示:  

Enter the trade name and quantities
or input q to sum: chicken 2
Enter the trade name and quantities
or input q to sum: fish 3
Enter the trade name and quantities
or input q to sum: duck 4
Enter the trade name and quantities  
  or input q to sum: q

Thu Jan 28 09:29:29 BJT 1999:
duck 4 10
fish 3 12.6
chicken 2 10.8

四、使用export语句
  通常shell变量是局部变量,无论是通过赋值操作还是通过命令赋值,其变量值只在当前进程中
有效。但是经过export语句说明的shell变量就成为一个全局性变量,其变量名和变量值可以传递给
子进程及其后代进程。在子进程中可以修改这个变量的值,但并不影响这个变量在父进程中的取值。  

  下面的例子中,父进程(/tmp/text)将赋值后的变量 pro_name传递给子进程(/tmp/text_child),
在子进程中对变量pro_name所赋的新值并不影响父进程中该变量的值。  

    /tmp/text程序:
pro_name=“PARENT"
echo “The variable pro_name is
$pro_name in parent process"
export pro_name
/tmp/text_child
echo “The variable pro_name is
$pro_name after retund to parent process"

    /tmp/text_child程序:
echo“The variable pro_name ($pro_name) is
      transmited to child process"
pro_name=“CHILD"
echo “To change the variable pro_name to  
   $pro_name in child process"
    执行程序/tmp/text:
$ /tmp/text
The variable pro_name is PARENT  
    in parent process
The variable pro_name (PARENT)
     is transmited to child process
To change the variable pro_name to CHILD  
    in child process
The variable pro_name is PARENT  
    after retund to parent process

quickleader 2004-7-16 17:08

Re:shell programming

介绍B-SHELL的一些运用方法
本文出自:http://www.fanqiang.com 作者: truename [luoli1974@163.com] (2001-08-22 17:30:12)
前言:

这里只介绍B-SHELL的一些运用方法,不对B-SHELL的基本知识进行说明(与其他的语言有很多的共性,果你懂一种语言的话,很快就能上手),如你是对B-SHELL不是很了解的话,而且又想学的话,建议你去查阅相关的资料(网上有很多关于这方面的东东;当然也可买些书;如果你是一个借别人的书才能阅读的人,那就去借吧!我就是这样的人之一。)。我的能力有限,若有不足或错误,请多多指教,小弟将感激不尽!所有的介绍适合于内核2.2.13的及以上的liunx环境。

B-SHELL是一个便捷的开发工具,在紧急情况下要实现产品的某一功能,而用C语言不能很快完成,则可选择B-SHLL实现,日后再用C语言完成;在产品里对于那些不常运行的程序可用B-SHELL替代,这样可以避免因对C语言使用不当而带来的不良后果;对于一些处理量不大的程序可用B-SHELL实现。


讨论:


#!/bin/sh à B-SHELL的解释器标志


ALL=$@

#{

B-SHELL中带”$”的变量的说明:$0à SHELL命令本身

$nà 位置参数n(n != 0)

$@à 所有的输入变量

$$à 当前进程号

$!à 最近后台进程的PID值

$?à 最近命令执行状态的返回值

$#à 参数的个数

}#

declare -x TIME=0

#{

变量声明的说明:


declare 声明全局变量;如果在函数内声明,则只在函数内有效(局部变量);-x表明变量值可以传递给后继命令(从函数传值给后继命令);-i表明变量作为整型处理;-r表明变量为只度;用“+”代替“-”,原有变量的声明屏蔽,恢复为无定义状态。由第一次赋值决定其参数在脚本中的性质,如果首次是字符,以后都当字符处理,其它等同。


local声明局部变量;local 变量名 [ = “值”];一般在函数内部运用。


直接定义变量,不用declare 和local指明。由第一次赋值决定其参数在脚本中的性质,如果首次是字符,以后都当字符处理,其它等同。也可在定义确定。

}#

declare -x NUMBEROFFILE=0

#{

关于脚本函数说明:

定义方式:function 函数名() 或 函数名()

{

函数体(作你想要作的事)

}

调用方式:函数名 参数列

在脚本里函数的调用就象调用命令一样使用。à 后面将会提到

}#


function hel_ver() à 显示帮助信息或版本号

{


if [ "$1" = "-h" ] ;then

echo "fstrf -v :show version of shell script."

echo "Usages : fstrf -h||-v "

echo " fstrf ARGUMENTS DIRECTORY STRING || fstrf ARGUMENTS STRING "

echo " For more informations run man fstrf."

exit

fi


if [ "$1" = "-v" ] || [ "$1" = "version" ];then

echo "fstrf version 1.0.1 2001/07/05."

exit

fi

}


function find_offer() à 在给定的目录下查找是否有包含指定字串的文件,并显示其路径

{

for file in `ls "$1"` à for var in command do ………done 的一个典型运用;对$1目录下的目录或文件进行处理

do

if [ -d "$1"/$file ];then à 判断$file是否为目录

continue

else

string=`grep "$2" "$1"/$file ` à 查找字串

if [ -n "$string" ];then à 判断字串为非空

echo $string > /str

str=`awk '{ print $1 }' /str` à 取得str 的第一域值

rm /str

if [ "$str" != "Binary" ];then à 剔除二进制文件

NUMBEROFFILE=`expr $NUMBEROFFILE + 1 ` à 相当于C语言的NUMBEROFFILE +=1;可用

let ‘ NUMBEROFFILE=NUMBEROFFILE + 1‘替换

TIME=`expr $TIME + 1`

echo "$1/$file" à 显示匹配文件

fi

fi

fi

done


}


#else

function find_curr() à 在当前目录下查找是否有包含指定字串的文件,并显示其路径(相对路径)实现机制与find_offer相同

{

# if [ "$#" -eq "1" ];then à

for file in `ls ./` 如果只输入一个参数,则查找当前工作目录下的文件

do

if [ -d "$file" ];then

continue

else

string=`grep "$1" $file`

if [ -n "$string" ];then

echo $string > /str

str=`awk '{ print $1 }' /str`

rm /str

if [ "$str" != "Binary" ];then

NUMBEROFFILE=`expr $NUMBEROFFILE + 1 `

TIME=`expr $TIME + 1`

echo "./$file"

fi

fi

fi

done

}

function print_num() à 显示查找的文件数 (在查多级目录时未能实现,如果你知道,告诉我,我会很高兴的!)

{

if [ "$NUMBEROFFILE" -gt "0" ] ;then

# echo ""

if [ "$#" -eq "1" ];then

#{

关于彩色显示的说明:

不同的linux系统,运行的命令的方式都不一样。在LINUX/BSD系统中:echo -e “\033[40;32m”;在V系统中:echo “\033[40;32m”。本例中采用前一种方式。40位为背景色,取值40&#0;&#0;47之间;32位为前景色,取值30&#0;&#0;37之间;\033是固定用法。彩色显示要成队匹配,如脚本所示。通常后一个色彩控制为

echo -e "\033[40;37m" (意思是恢复正常色)。彩色显示控制通常是成对出现,例如,

`echo -e "\033[40;33m"`"$2"`echo -e "\033[40;37m"`

echo -e "\033[40;37m"à 恢复正常显示(黑背景,白前景)。利用彩色显示控可以作出漂亮的liunx菜单(当然不能与Xwindows和WINDOWS相比)。

}#

echo "Finded `echo -e "\033[40;32m"`$NUMBEROFFILE`echo -e "\033[40;37m"` file(s) which have the string `echo -e "\033[40;33m"`"$1"`echo -e "\033[40;37m"` ."

else

echo "Finded `echo -e "\033[40;32m"`$NUMBEROFFILE`echo -e "\033[40;37m"` file(s) which have the string `echo -e "\033[40;33m"`"$2"`echo -e "\033[40;37m"`. "

fi

else

if [ -z "$2" ];then

echo "Finded `echo -e "\033[40;32m"`no `echo -e "\033[40;37m"`file under current directory."

else

echo "Finded `echo -e "\033[40;32m"`no `echo -e "\033[40;37m"`file under directory "$1"."

fi

fi

}

function fdir() à 从给定的目录开始查找当前目录及其子目录的包含的目录,并存入/tmp/dtmp文件。

{

>/tmp/dtmp

#cd /

echo "$1" >/tmp/dtmp

tmp=`echo $1 | grep "\/proc"`à 不查找/proc目录下的目录

if [ ! -z $tmp ];then

# echo "/proc"

exit

fi

i=`expr $i + 1 ` à 记录找到的目录数目,每向/tmp/dtmp文件加一目录记录时此值加一(以下同样)

###first grade

cd $1 à 进入脚本的工作目录(不会改变控制台的工作目录)

for loop in `ls ./`

do

# echo $loop

if [ -d $loop ];then à 判断$loop是否为目录(以下同样)

tmp=`echo $loop | grep "proc"`

if [ ! -z $tmp ];then

continue

fi

i=`expr $i + 1 `

echo "`pwd`/$loop" >>/tmp/dtmp à 连同目录的绝对路径加入/tmp/dtmp文件(以下同样)

###second grade

cd $loop à 进入脚本二级工作目录

for loop1 in `ls ./`

do

# echo $loop1

if [ -d $loop1 ];then

tmp=`echo $loop1 | grep "proc"`

if [ ! -z $tmp ];then

continue

fi

i=`expr $i + 1 `

echo "`pwd`/$loop1" >>/tmp/dtmp

###third grade

cd $loop1à 进入脚本三级工作目录

for loop1 in `ls ./`

do

# echo $loop1

if [ -d $loop1 ];then

i=`expr $i + 1 `

echo "`pwd`/$loop1" >>/tmp/dtmp

…………

………

………

………

…………

else

continue

fi

done

cd .. à 退出脚本三级工作目录,进入脚本二级工作目录

else

continue

fi

done

cd ..à 退出脚本二级工作目录,进入脚本一级工作目录

else

continue

fi

done

}


#{

test的用法说明:在脚本里,用一对[ ]表示test,如[ "$#" -eq "0" ]。注意:参数与[]之间一定要有空格(最好是所有的参数之间都要有空格)。如果不了解test参数的用法,运行man test,只要你学过英语你一定能看懂;实在没有办法,那就上网去查吧!

}#

##main shell

if [ "$#" -eq "0" ];then

echo "`echo -e "\033[40;33m"`run fstrf -h or man fstrf `echo -e "\033[40;37m"`to get more help informations."

echo "`echo -e "\033[40;36m"`list current directory:`echo -e "\033[40;37m"`"

ls ./

echo "`echo -e "\033[40;36m"`no string input!!!`echo -e "\033[40;37m"`"

exit

fi

hel_ver $ALL à 调用hel_ver函数

if [ "$1" = "-up" ];then à 查找指定当前目录内及字目录内的文件

if [ "$#" -eq "3" ];then

fdir $2 $3 2>/dev/null 1>&2 à 将所有的错误输出均输向/dev/null(不在屏幕上回显,以下一样)

#{

while read var do .. done的重定向说明:

有两种方式: 1. while read var do .. done < 重定向文件的路径及文件名

while read var

do

..

done < 重定向文件的路径及文件名

2. dd if=重定向文件的路径及文件名 bs=512 2>/dev/null | while read var do .. done

dd if=重定向文件的路径及文件名 bs=512 2>/dev/null | \

while read var

do

..

done


}#

#{

关于函数调用的说明:

在脚本中函数的调用类似与LINUX命令的调用,只是变量要用“”括起来。函数内部的$n只相对与函数的参数位置,如find_offer "$DIR" "$3",find_offer内的$1对应于"$DIR",$2对应于"$3",与SHELL 主程序的$n无对应关系,既函数的$1不一定要等于SHELL主程序的$1。

}#

dd if=/tmp/dtmp bs=512 2>/dev/null | \

while read DIR

do

find_offer "$DIR" "$3"

done

# print_num "$2" "$3"

fi

if [ "$#" -eq "2" ];then à 查找指定当前工作目录内及字其目录内的文件

fdir ./ 2>/dev/null 1>&2

dd if=/tmp/dtmp bs=512 2>/dev/null | \

while read DIR

do

find_offer "$DIR" "$2"

done

# print_num "$2"

fi

fi

if [ "$1" = "-cu" ];then à 查找当前目录或指定当前目录内的文件

if [ "$#" -eq "2" ];then à 查找当前目录匹配的文件

find_curr "$2"

print_num "$2" à 显示匹配的文件数

fi

if [ "$#" -eq "3" ];then à 查找指定当前目录内的文件

find_offer "$2" "$3"

print_num "$3" à 显示匹配的文件数

fi

fi

if [ -e /tmp/dtmp ];then à 文件存在则删除

rm /tmp/dtmp

fi

quickleader 2004-7-16 17:09

Re:shell programming

shell编程例子 -- 一个简单的目录菜单
本文出自: 作者: (2001-10-01 08:00:00)
until
    echo List Directory..........1
    echo Change Directory........2
    echo Edit File...............3
    echo Remove File.............4
    echo Exit Menu...............5

    read choice
    test $choice = 5
    do
        case $choice in
        1) ls;;
        2) echo Enter target directory
           read dir
           cd $dir
           ;;
        3) echo Enter file name
           read file
           vi $file
           ;;
        4) echo Enter file name
           read file
           rm $file
           ;;
        q|Q|5) echo Goodbye;;
        *) echo illegal Option
    esac
done

quickleader 2004-7-16 17:09

Re:shell programming

shell编程例子 -- 一个.cshrc例子
本文出自: 作者: (2001-10-01 09:00:00)
*****************************************************************
# * *
# * *
# *****************************************************************
#
# HISTORY
#  
# @(#)$RCSfile: .cshrc,v $ $Revision: 4.1.3.3 $ (DEC) $Date: 1992/05/11 09:13:09  
$  
#  
setenv MAIL /usr/spool/mail/$USER
#******************************************************************************
#******************************************************************************
#
# add by zzy 2000.08.06
#
#******************************************************************************
#******************************************************************************
# set pics environment
#******************************************************************************
#source /etc/pics.d/picsenv.csh
#******************************************************************************
# set innovator environment
#******************************************************************************
#source /usr/innovator/inoenv.csh
#******************************************************************************
# set PCFC application environment
#******************************************************************************
source ${ HOME}/app_login
#******************************************************************************
# change path to home path  
#******************************************************************************
cd ${ HOME}
#******************************************************************************
app_login:
==========
#******************************************************************************
#* module: app_login
#* description: app login
#* This script must be called in the login file
#* with the command 'source app_login'
#* author: zzy
#* version: 31.08.1998
#******************************************************************************

#******************************************************************************
# entry
#******************************************************************************

#******************************************************************************
# environment
#******************************************************************************
set say = "echo"
set mode = ""
set node = "`uname -n`"
set user = "`logname`"

#******************************************************************************
# this path and this script
#******************************************************************************
set this_path1 = "`dirname ${ HOME}/x`"
set this_script1 = "app_login"

if ( ! -f ${ this_path1}/${ this_script1} ) then
$say 'this script must be in the directory $HOME'
goto exit
endif

#******************************************************************************
# start message
#******************************************************************************
$say " %$this_script1, `date '+%H:%M:%S'` start executing on $node"

#******************************************************************************
# set project and channel prefix for all user
#******************************************************************************
if ( "$user" == "root" ) then
setenv PROJ pcfc
setenv CPRE ""
endif

if ( "$user" == "pcfc" ) then
setenv PROJ pcfc
setenv CPRE ""
endif

if ( "$user" == "picsadm" ) then
setenv PROJ baoshan
setenv CPRE ""
endif

if ( "$user" == "picsvis" ) then
setenv PROJ baoshan
setenv CPRE ""
endif

if ( "$user" == "" ) then
setenv PROJ pcfc
setenv CPRE ""
endif

#******************************************************************************
# set project directory
#******************************************************************************
setenv PROJ_DIR /home/$PROJ

#******************************************************************************
# call project application login
#******************************************************************************
if ( -f "${ this_path1}/${ this_script1}_$PROJ" ) then
$say " %$this_script1, call project application login"
source ${ this_path1}/${ this_script1}_$PROJ
endif

#******************************************************************************
# end message
#******************************************************************************
#$say " %$this_script1, `date '+%H:%M:%S'` finished"

#******************************************************************************
# exit
#******************************************************************************
exit:
unset this_path1
unset this_script1
exit

#******************************************************************************

app_login_pcfc
==============
#******************************************************************************
#* module: application_login_$PROJ
#* description: application login for a project
#* This script must be called in the login file app_login
#* with the command 'source app_login_$PROJ'
#* author: Zhou zeyan  
#*  
#* version: 8.1.2000
#******************************************************************************

#******************************************************************************
# entry
#******************************************************************************

#******************************************************************************
# environment
#******************************************************************************
set say = "echo"
set mode = ""
set node = "`uname -n`"
set user = "`logname`"

#******************************************************************************
# this path and this script
#******************************************************************************
set this_path2 = "`dirname ${ HOME}/x`"
set this_script2 = "app_login_$PROJ"

#******************************************************************************
# start message
#******************************************************************************
$say " %$this_script2, `date '+%H:%M:%S'` start executing on $node"

#******************************************************************************
# history
#******************************************************************************
set history=40
alias h history

#******************************************************************************
# aliases
#******************************************************************************
alias sql sqlplus pcfcadm/pcfcadm

#******************************************************************************
# set environment for components
#******************************************************************************

set cmp = glb
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = utl
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = com
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = log
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = tel  
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = mva
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = mtr
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = mm
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = de
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = se
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = comtcp
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
set cmp = sdd
if ( -r "${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init" ) then
source ${ PROJ_DIR}/${ cmp}/etc/${ cmp}_init
endif
unset cmp

#******************************************************************************
# set environment for project
#******************************************************************************
if ( -r "${ PROJ_DIR}/login/login" ) then
source ${ PROJ_DIR}/login/login
endif

#******************************************************************************
# end message
#******************************************************************************
#$say " %$this_script2, `date '+%H:%M:%S'` finished"

#******************************************************************************
# unset
#******************************************************************************
exit:
unset this_path2
unset this_script2
exit
#******************************************************************************

login
========
#******************************************************************************
#* module: login
#* description: project login definitions
#* author: Zhou zeyan
#* version: 31.08.2000
#******************************************************************************

#******************************************************************************
# entry
#******************************************************************************

#******************************************************************************
# environment
#******************************************************************************
set say = echo
set mode = ""
set node = "`uname -n`"
set user = "`logname`"

#******************************************************************************
# pathes
#******************************************************************************
set path_project_login = "${ PROJ_DIR}/login"
set path_project_com = "${ path_project_login}/com"

#******************************************************************************
# definitions make
#******************************************************************************
if ( -r "${ path_project_com}/def_make" ) then
source ${ path_project_com}/def_make
endif

#******************************************************************************
# definitions path
#******************************************************************************
if ( -r "${ path_project_com}/def_path" ) then
source ${ path_project_com}/def_path
endif

#******************************************************************************
# definitions unix commands
#******************************************************************************
if ( -r "${ path_project_com}/def_unix" ) then
source ${ path_project_com}/def_unix
endif

#******************************************************************************
# definitions user commands
#******************************************************************************
if ( -r "${ path_project_com