关于作者

用户名:colding
笔名:colding
地区: 中国-北京
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:


shadow

shadow的blog

Center of STL Study

最优秀的STL使用学习网站

C++之dozb的程序人生

BOOSTt学习网站

最新评论

msdn library

访问统计:
文章个数:181
评论个数:3
留言条数:18




Powered by BlogDriver 2.1

colding forever

 

welcome to my blog

文章

浅谈C中的malloc和free
摘要:malloc和free 查看全文

- 作者: colding 2007年08月31日, 星期五 18:09  回复(0) |  引用(0) 加入博采

C和C++中如何互相调用(#ifdef __cplusplus)
摘要:C和C++中如何互相调用(#ifdef __cplusplus) 查看全文

- 作者: colding 2007年08月16日, 星期四 11:24  回复(0) |  引用(0) 加入博采

#ifdef的用法
摘要:灵活使用#ifdef指示符,我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码。 查看全文

- 作者: colding 2007年08月14日, 星期二 10:26  回复(0) |  引用(0) 加入博采

shell精华集锦【转】
01) shell编程入门及脚本测试
02) shell脚本欣赏区
03) TCSH shell编程入门
04) Bash命令行处理[详解]
05) shell命令基础
06) Linux命令大集合
07) sed/awk与unix命令等价代码欣赏
08) 如何将数字转换为大写数字?
09) 出道算术题考考你?
10) 考考你?[关于数组]
11) 如何横排变竖排?
12) 关于屏幕定位显示
13) 如果确定某个特定字符在一个字串中的位置
14) 如何用shell产生随机数组
15) 如何处理键盘输入超时
16) 1到999999999的阵列,如何用shell实现?
17) 用dd命令去备份文件指定的部分
18) 一个自动解压缩rar的脚本
19) UNIXshell/LINUXshell--实例精解源代码
20) 常用的关机和重新启动命令
21) GNU 文本实用程序教程
22) 关于array中的变量替换? [分享]
23) 在bash下实现双窗口或多窗口的方法
24) awk引用变量的问题
25) awk命令实用详解
26) RPM常用命令及RPM中文手册
27) [精彩讨论]sed/awk的新问题
28) 优先级[nice/renice实例]
29) 制作像 nvidia驱动包 那样的可执行安装包
30) Shell的命令行编辑[vi&emacs命令行技巧征集]
31) 如何把一个命令的输出放进多个管道里去,并让那些管道命令并行化
32) 有什么好办法在解压缩过程中同时获取压缩包的内容目录结构的顶层目录呢?
33) Linux Shell简介(入门)
34) BASH最常见的激活模式
35) 论坛是否有expect的技术介绍[附:Expect中文教程]
36) 变态vi问答
37) 如何产生一个随机的字母和一个一位的数字?[已解决]
38) shell编程中${ }的用法[转]
39) 适用于ADSL的连接脚本。
40) 文本模式下手动画表格的脚本
41) grep,sed,awk命令实例大练习[大力推荐]
42) 一个小脚本,帮助你清除无用的rpm包(zt)
43) 关于网络流量统计
44) bash_profile和.bashrc有什么区别?[附:几个bash配置文件的说明]
45) 一个连网的实用脚本
46) ADSL上网脚本
47) slackware添加用户adduser脚本欣赏
48) Gawk手册
49) latex2pdf的脚本
50) BASH中字符串的处理[转]
51) 这个脚本怎么写?[about find&xargs]
52) ulimit使用方法
53) 几个shell自动化脚本
54) 判断XDSL上网方式的用户的网络工作情况的脚本
55) 自动ftp脚本[附录ftp命令大全]
56) 关于zero及NULL设备的讨论57) 硬连接和软连接的区别的讨论[附:ln中文文档]
58) 将某一目录下以main开头的100个文件的文件名分别改为main1到main100
59) 搜索一首歌并调用XMMS播放的脚本
60) 关于如何设定控制台的彩色显示?
61) 我用DF命令后看到这样的信息是咋回事[附:各种文件系统介绍]
62) Linux [系统管理命令中文详解][转]
63) bash脚本调试器
64) 使用 /proc 文件系统来控制系统[转贴]
65) 如何在shell中将unix时间戳转换为系统时间
66) "2>&1" 是什么意思?[重定向问题合并]
67) 拿到一个好的五笔98码表+awk一例[合并]
68) rh9自动运行X程序的方法
69) MOUNT命令中各个参数的含义
70) 如何从HTML文件中提取charset
71) BASH shell set命令详解
72) VI高级命令集锦及VIM应用实例
73) Linux/UNIX命令dd简介
74) 怎么让cp显示拷贝的进度或者速度?
75) 如何将一个输出中的若干行分别赋值给变量?
76) 修改了一下startx,用于窗口管理器的选择
77) 我想把下面的人名改成第一个字母大字,其它小写
78) 系统备份用的脚本
79) [转贴]编译APACHE+PHP+MYSQL的一个脚本
80) shell脚本是否可编译成二进制文件????<讨论>
81) 如何阻止非root用户再shell下用Ctrl+Alt+Delete重启电脑
82) tr的使用方法[转贴]
83) 设置终端--stty使用方法[转贴]
84) BASH-HOWTO文档
85) 解读startx
86) 写了一个显示目录树的脚本,可输出不对
87) 如何開一term ?K在里头顯某個文件的內容??
88) 如何在脚本中应用dialog命令<欢迎讨论>
89)
90) find和xargs使用详解[转自《shell编程和unix命令》]
91) Redhat8 下脚本的一些问题
92) 删除文件跟恢复文件系列。
93) 防止误删文件的脚本
94) 熟练运用命令所必须的<正则表达式语法>
95) linux shell trap信号表
96) shell入门基础必备
97) 如何快速把带有大写字母的文件名改为全部小写?
98) Unix Shell 介绍 (author:S. R. Bourne)
99) 技巧:提示行魔术
100) 禁止用户登录脚本
101) 一个自动挂载dos分区的脚本
101) bash快捷键盘使用心得,非转载~~~
102) 几个可以加在SHELL配置脚本里面的小函数
103) 如何用bash提取网卡ip地址?
104) shell技巧交流区
105) 适合初学练习的一组小脚本
106) Linux 终端的彩色显示
107) 一个自动解压缩rar的脚本
108) 一道算术题/font>
109) 如何把一个文件倒着读出来?
110) source命令的一个妙用
111) 压缩-分段-备份的实现
112) 设置命令提示符下的热键[转]
113) BASH教学实例
114) 用sed/awk合并两行
115) stty中文手册
116) VIM速查手册
117) VIMset选项详解
118) 自动mount各类分区的脚本
119) shell十三问(简体版PDF)
120) dialog中文man手册
121) 怎样得到终端的参数如长宽行数?
122) 判断一个数是否2的幂[讨论awk的扩展用途]
123) tr:超级工具:为数据做外科手术(dd, head, tail...)
124) 服务器诊所: Expect 超出预期
125) 再来一个tree脚本(原创)
126) 几种shell里的小括号,大括号结构和有括号的变量,命令的用法
127) bash中字符串的处理(参阅CU一贴后重整理)
128) Linux与unix shell编程学习体会(根据书和其他帖子整理)
129) 管理脚本语言
130)***sed新手使用进阶全功略!***
131) Linux解释器原理
132) RE,grep,sed,awk论坛题目总结!(不断整理中......)
待续...

- 作者: colding 2006年09月8日, 星期五 11:22  回复(2) |  引用(0) 加入博采

正则表达式基础
我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串,搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感,单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说:
1.1 句点符号
假 设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束。另外,假设有一本英文字典,你可以用正则表达式搜 索它的全部内容。要构造出这个正则表达式,你可以使用一个通配符——句点符号“.”。这样,完整的表达式就是“t.n”,它匹配“tan”、“ten”、 “tin”和“ton”,还匹配“t#n”、“tpn”甚至“t n”,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符:
1.2 方括号符号
为 了解决句点符号匹配范围过于广泛这一问题,你可以在方括号(“[]”)里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说, 正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为在方括号之内你只能匹配单个字符:
1.3 “或”符号
如 果除了上面匹配的所有单词之外,你还想要匹配“toon”,那么,你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”, 使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号,因为方括号只允许匹配单个字符;这里必须使用圆括号“()”。圆括号还可以用来分 组,具体请参见后面介绍。
1.4 表示匹配次数的符号
表一显示了表示匹配次数的符号,这些符号用来确定紧靠该符号左边的符号出现的次数:

假 设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中,连字符 (“-”)有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“\”。


图一:匹配所有123-12-1234形式的社会安全号码
假设进行搜索的时候,你希望连字符号可以出现,也可以不出现——即,999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后面加上“?”数量限定符号,如图二所示:


图二:匹配所有123-12-1234和123121234形式的社会安全号码
下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{4}”,再加上字母部分“[A-Z]{2}”。图三显示了完整的正则表达式。


图三:匹配典型的美国汽车牌照号码,如8836KV
1.5 “否”符号
“^”符号称为“否”符号。如果用在方括号内,“^”表示不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外。


图四:匹配所有单词,但“X”开头的除外
1.6 圆括号和空白符号
假设要从格式为“June 26, 1951”的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如图五所示:


图五:匹配所有Moth DD,YYYY格式的日期
新出现的“\s”符号是空白符号,匹配所有的空白字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用ORO API(本文后面详细讨论)提取出它的值。修改后的正则表达式如图六所示:


图六:匹配所有Month DD,YYYY格式的日期,定义月份值为第一个组
1.7 其它符号
为简便起见,你可以使用一些为常见正则表达式创建的快捷符号。如表二所示:
表二:常用符号

例如,在前面社会安全号码的例子中,所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示:


图七:匹配所有123-12-1234格式的社会安全号码

- 作者: colding 2006年09月5日, 星期二 18:26  回复(0) |  引用(0) 加入博采

三个UNIX文件时间ctime,mtime,atime(转)
当你同熟练的UNIX用户进行交谈时,你经常会听到他们傲慢地讲出术语“改变时间(change time)”和“修改时间(modification time)”。对于许多人(和许多字典而言),改变和修改是相同的。这里会有什么不同那?

改变和修改之间的区别在于是改某个组件的标签还是更改它的内容。如果有人说chmod a-w myfile,那么这是一个改变;如果有人说echo foo >;>;myfile。则这是一个修改。改变是文件的索引节点发生了改变;修改是文本本身的内容发生了变化。[文件的修改时间也叫时间标志 (timestamp).]

只要讨论改变时间和修改时间,就不可能不提到“访问时间(access time)”.访问时间是文件最后一次被读取的时间。因此阅读一个文件会更新它的访问时间,当它的改变时间并没有变化(有关文件的信息没有被改变),它的修改时间也同样如此(文件本身没有被改变)

有时,在许多地方改变时间或者“ctime”被错误地写成“创建时间”,包括某些UNIX参考手册。不要相信他们

- 作者: colding 2006年08月31日, 星期四 16:09  回复(0) |  引用(0) 加入博采

Linux中文件查找技术大全
shell基础二:查找技巧,find及xargs的使用
http://bbs.chinaunix.net/forum/v ... p;highlight=wingger

通过文件名查找法:
这个方法说起来就和在WINDOWS下查找文件一样容易理解了。如果你把这个文件放在单个的文件夹里面,只要使用常见的“ls"命令就能方便的查找出来, 那么使用“find”命令来查找它就不能给你留下深刻的印象,毕竟“find”命令的强大功能不止这个。如果知道了某个文件的文件名,而不知道这个文件放 到哪个文件夹,甚至是层层套嵌的文件夹里。举例说明,假设你忘记了httpd.conf这个文件在系统的哪个目录下,甚至在系统的某个地方也不知道,则这 是可以使用如下命令:
find / -name httpd.conf

这个命令语法看起来很容易就明白了,就是直接在find后面写上 -name,表明要求系统按照文件名查找,最后写上httpd.conf这个目标文件名即可。稍等一会系统会在计算机屏幕上显示出查找结果列表:
etc/httpd/conf/httpd.conf

这就是httpd.conf这个文件在Linux系统中的完整路径。查找成功。
如果输入以上查找命令后系统并没有显示出结果,那么不要以为系统没有执行find/ -name httpd.conf命令,而可能是你的系统中没有安装Apache服务器,这时只要你安装了Apache Web服务器,然后再使用find / -name httpd.conf就能找到这个配置文件了。

无错误查找技巧:
在Linux系统中“find”命令是大多数系统用户都可以使用的命令,并不是ROOT系统管理员的专利。但是普通用户使用“find”命令时也有可能遇 到这样的问题,那就是Linux系统中系统管理员ROOT可以把某些文件目录设置成禁止访问模式。这样普通用户就没有权限用“find”命令来查询这些目 录或者文件。当普通用户使用“find”命令来查询这些文件目录是,往往会出现"ermissiondenied."(禁止访问)字样。系统将无法查询到你想要的文件。为了避免这样的错误,我们可是使用转移错误提示的方法尝试着查找文件,输入
find / -name access_log 2>;/dev/null

这个方法是把查找错误提示转移到特定的目录中去。系统执行这个命令后,遇到错误的信息就直接输送到stderrstream 2 中,access_log 2就是表明系统将把错误信息输送到stderrstream 2中,/dev/null是一个特殊的文件,表明空的或者错误的信息,这样查询到的错误信息将被转移了,不会再显示了。

在Linux系统查找文件也会遇到这样一个实际问题。如果我们在整个硬盘,这个系统中查找某个文件就要花费相当长的一段时间,特别是大型Linux系统和 容量较大的硬盘,文件放在套嵌很深的目录中的时候。如果我们知道了这个文件存放在某个大的目录中,那么只要在这个目录中往下找就能节省很多时间了。使用 find /etc -name httpd.conf 就可以解决这个问题。上面的命令就是表示在etc目录中查询httpd.conf这个文件。这里再说明一下“/ ”这个函数符号的含义,如果输入 “find/ ”就是表示要求Linux系统在整个ROOT目录下查找文件,也就是在整个硬盘上查找文件,而“find/etc”就是只在 etc目录下查找文件。因为“find/etc”表示只在etc目录下查找文件,所以查找的速度就相应要快很多了。

根据部分文件名查找方法:
这个方法和在WINDOWS中查找已知的文件名方法是一样的。不过在Linux中根据部分文件名查找文件的方法要比在WINDOWS中的同类查找方法要强 大得多。例如我们知道某个文件包含有srm这3个字母,那么要找到系统中所有包含有这3个字母的文件是可以实现的,输入:
find /etc -name '*srm*'

这个命令表明了Linux系统将在/etc整个目录中查找所有的包含有srm这3个字母的文件,比如 absrmyz, tibc.srm等等符合条件的文件都能显示出来。如果你还知道这个文件是由srm 这3个字母打头的,那么我们还可以省略最前面的星号,命令如下:
find/etc -name 'srm*'  

这是只有像srmyz 这样的文件才被查找出来,象absrmyz或者 absrm这样的文件都不符合要求,不被显示,这样查找文件的效率和可靠性就大大增强了。

根据文件的特征查询方法:
如果只知道某个文件的大小,修改日期等特征也可以使用“find”命令查找出来,这和WINDOWS系统中的"搜索"功能是基本相同的。在微软的"搜索" 中WINDOWS中的"搜索助理"使得搜索文件和文件夹、打印机、用户以及网络中的其他计算机更加容易。它甚至使在Internet 上搜索更加容易。"搜索助理"还包括一个索引服务,该服务维护了计算机中所有文件的索引,使得搜索速度更快。使用"搜索助理"时,用户可以指定多个搜索标 准。例如,用户可以按名称、类型及大小搜索文件和文件夹。用户甚至可以搜索包含特定文本的文件。如果用户正使用 Active Directory,这时还可以搜索带有特定名称或位置的打印机。

例如我们知道一个Linux文件大小为1,500 bytes,那么我们可是使用如下命令来查询find / -size 1500c,字符 c 表明这个要查找的文件的大小是以bytes为单位。如果我们连这个文件的具体大小都不知道,那么在Linux中还可以进行模糊查找方式来解决。例如我们输 入find/ -size +10000000c 这个命令,则标明我们指定系统在根目录中查找出大于10000000字节的文件并显示出来。命令中的“+”是表示要求系统只列出大于指定大小的文件,而使 用“-”则表示要求系统列出小于指定大小的文件。下面的列表就是在Linux使用不同“ find"命令后系统所要作出的查找动作,从中我们很容易看出在Linux中使用“find"命令的方式是很多的,“ find"命令查找文件只要灵活应用,丝毫不必在WINDOWS中查找能力差。

find / -amin -10 # 查找在系统中最后10分钟访问的文件
find / -atime -2 # 查找在系统中最后48小时访问的文件
find / -empty # 查找在系统中为空的文件或者文件夹
find / -group cat # 查找在系统中属于 groupcat的文件
find / -mmin -5 # 查找在系统中最后5分钟里修改过的文件
find / -mtime -1 #查找在系统中最后24小时里修改过的文件
find / -nouser #查找在系统中属于作废用户的文件
find / -user fred #查找在系统中属于FRED这个用户的文件


下面的列表就是对find命令所可以指定文件的特征进行查找的部分条件。在这里并没有列举所有的查找条件,参考有关Linux有关书籍可以知道所有find命令的查找函数。
-amin n
查找系统中最后N分钟访问的文件
-atime n
查找系统中最后n*24小时访问的文件
-cmin n
查找系统中最后N分钟被改变状态的文件
-ctime n
查找系统中最后n*24小时被改变状态的文件
-empty
查找系统中空白的文件,或空白的文件目录,或目录中没有子目录的文件夹
-false
查找系统中总是错误的文件
-fstype type
查找系统中存在于指定文件系统的文件,例如:ext2 .
-gid n
查找系统中文件数字组 ID 为 n的文件
-group gname
查找系统中文件属于gnam文件组,并且指定组和ID的文件


Find命令的控制选项说明:
Find命令也提供给用户一些特有的选项来控制查找操作。下表就是我们总结出的最基本,最常用的find命令的控制选项及其用法。
选项
用途描述
-daystart
.测试系统从今天开始24小时以内的文件,用法类似-amin
-depth
使用深度级别的查找过程方式,在某层指定目录中优先查找文件内容
-follow
遵循通配符链接方式查找&#59; 另外,也可忽略通配符链接方式查询
-help
显示命令摘要
-maxdepth levels
在某个层次的目录中按照递减方法查找
-mount
不在文件系统目录中查找, 用法类似 -xdev.
-noleaf
禁止在非UNUX文件系统,MS-DOS系统,CD-ROM文件系统中进行最优化查找
-version
打印版本数字


使用-follow选项后,find命令则遵循通配符链接方式进行查找,除非你指定这个选项,否则一般情况下find命令将忽略通配符链接方式进行文件查找。

-maxdepth选项的作用就是限制find命令在目录中按照递减方式查找文件的时候搜索文件超过某个级别或者搜索过多的目录,这样导致查找速度变慢,查找花费的时间过多。例如,我们要在当前(.)目录技巧子目录中查找一个名叫fred的文件,我们可以使用如下命令
find . -maxdepth 2 -name fred

假如这个fred文件在./sub1/fred目录中,那么这个命令就会直接定位这个文件,查找很容易成功。如,这个文件在. /sub1/sub2/fred目录中,那么这个命令就无法查找到。因为前面已经给find命令在目录中最大的查询目录级别为2,只能查找2层目录下的文 件。这样做的目的就是为了让find命令更加精确的定位文件,如果你已经知道了某个文件大概所在的文件目录级数,那么加入-maxdepth n 就很快的能在指定目录中查找成功。

使用混合查找方式查找文件
find命令可以使用混合查找的方法,例如我们想在/tmp目录中查找大于100000000字节并且在48小时内修改的某个文件,我们可以使用-and 来把两个查找选项链接起来组合成一个混合的查找方式。
find /tmp -size +10000000c -and -mtime +2

学习过计算机语言的朋友都知道,在计算机语言里,使用and ,or 分别表示“与”和“或”的关系。在Linux系统的查找命令中一样通用。
还有这样的例子,
find / -user fred -or -user george

我们可以解释为在/tmp目录中查找属于fred或者george这两个用户的文件。
在find命令中还可以使用“非”的关系来查找文件,如果我们要在/tmp目录中查找所有不属于panda的文件,使用一个简单的
find /tmp ! -user panda  
命令就可以解决了。很简单。

查找并显示文件的方法
查找到某个文件是我们的目的,我们更想知道查找到的文件的详细信息和属性,如果我们采取现查找文件,在使用LS命令来查看文件信息是相当繁琐的,现在我们也可以把这两个命令结合起来使用。
find / -name "httpd.conf" -ls

系统查找到httpd.conf文件后立即在屏幕上显示httpd.conf文件信息。
12063 34 -rw-r--r-- 1 root root 33545 Dec 30 15:36 /etc/httpd/conf/httpd.conf

下面的表格就是一些常用的查找文件并显示文件信息的参数和使用方法
选项
用途描述
-exec command&#59;
查找并执行命令
-fprint file
打印文件完整文件名
-fprint0 file
打印文件完整文件名包括空的文件
-fprintf file format
打印文件格式
-ok command&#59;
给用户命令执行操作,根据用户的Y 确认输入执行
-printf format
打印文件格式
-ls
打印同种文件格式的文件.

xargs

      在使用f i n d命令的- e x e c选项处理匹配到的文件时, f i n d命令将所有匹配到的文件一起传递给e x e c执行。但有些系统对能够传递给e x e c的命令长度有限制,这样在f i n d命令运行几分钟之后,就会出现溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是x a rg s命令的用处所在,特别是与f i n d命令一起使用。

       F i n d命令把匹配到的文件传递给x a rg s命令,而x a rg s命令每次只获取一部分文件而不是全部,不像- e x e c选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。

      在有些系统中,使用- e x e c选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;
       而使用x a rg s命令则只有一个进程。另外,在使用x a rg s命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。

      来看看x a rg s命令是如何同f i n d命令一起使用的,并给出一些例子。

      下面的例子查找系统中的每一个普通文件,然后使用x a rg s命令来测试它们分别属于哪类文件

CODE:

#find . -type f -print | xargs file
./.kde/Autostart/Autorun.desktop: UTF-8 Unicode English text
./.kde/Autostart/.directory:      ISO-8859 text\
......
在整个系统中查找内存信息转储文件(core dump) ,然后把结果保存到/tmp/core.log 文件中:

CODE:

$ find / -name "core" -print | xargs echo "" >/tmp/core.log
上面这个执行太慢,我改成在当前目录下查找

CODE:

#find . -name "file*" -print | xargs echo "" > /temp/core.log
# cat /temp/core.log
./file6
在当前目录下查找所有用户具有读、写和执行权限的文件,并收回相应的写权限:

CODE:


# ls -l
drwxrwxrwx    2 sam      adm          4096 10月 30 20:14 file6
-rwxrwxrwx    2 sam      adm             0 10月 31 01:01 http3.conf
-rwxrwxrwx    2 sam      adm             0 10月 31 01:01 httpd.conf

# find . -perm -7 -print | xargs chmod o-w
# ls -l
drwxrwxr-x    2 sam      adm          4096 10月 30 20:14 file6
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 http3.conf
-rwxrwxr-x    2 sam      adm             0 10月 31 01:01 httpd.conf
用g r e p命令在所有的普通文件中搜索hostname这个词:

CODE:

# find . -type f -print | xargs grep "hostname"
./httpd1.conf:#     different IP addresses or hostnames and have them handled by the
./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
on your
用g r e p命令在当前目录下的所有普通文件中搜索hostnames这个词:

CODE:

# find . -name \* -type f -print | xargs grep "hostnames"
./httpd1.conf:#     different IP addresses or hostnames and have them handled by the
./httpd1.conf:# VirtualHost: If you want to maintain multiple domains/hostnames
on your

- 作者: colding 2006年08月31日, 星期四 16:00  回复(0) |  引用(0) 加入博采

用FUSE开发自己的文件系统二 [转]

定义 RX RPC

在更进一步之前, 你需要定一个你的RX RPC . 为了做到这个, 要为rxgen创建一个.xg 文件, 来描述你的将用afsfuse_client.c afsfuse_server.c被连接起来的代理和桩代码. Listing 2 描述了怎么创建含有以下内容的afsfuse.xg 文件:


Listing 2. 创建一个afsfuse.xg 文件

#define MYMAXPATH 512

#include <rx/rx.h>

#include </rx_null.h >

#define SAMPLE_SERVER_PORT 5000

#define SAMPLE_SERVICE_PORT 0

/* i.e. user server's port */

#define SAMPLE_SERVICE_ID 4 /* Maximum number of requests that will be handled by this

                                service simultaneously */

/* This number will also be guaranteed to execute in parallel if no services' requests

   are being processed */

#define SAMPLE_MAX 2 /* Minimum number of requests that are guaranteed to be handled

                         immediately */

#define SAMPLE_MIN 1 /* Index of the "null" security class in the sample service. This

                         must be 0 (there are N classes, numbered from 0. In this case,

                         N is 1) */

#define SAMPLE_NULL 0 /********************** fuse4_file_info taken from fuse.h the

                        rxgen does not understands fuse.h  mystat taken from man 2

                        mystat these are required again rxgen does not understand the

                        struct paras and will bump.. **********************/

struct my_file_info { /** Open flags. Available in open() and release() */

                     int flags; /** File handle. May be filled in by filesystem in

                                    open(). Available in all other file operations */

                     unsigned int fh; /** In case of a write operation indicates if

                         &160;                this was caused by a writepage */

                     int writepage;

                    };

struct mystatfs {

                    afs_uint32 f_type; /* type of filesystem (see below) */

                    afs_uint32 f_bsize; /* optimal transfer block size */

                    afs_uint32 f_blocks; /* total data blocks in file system */

                    afs_uint32 f_bfree; /* free blocks in fs */

                    afs_uint32 f_bavail; /* free blocks avail to non-superuser */

                    afs_uint32 f_files; /* total file nodes in file system */

                    afs_uint32 f_ffree; /* free file nodes in fs */

                    afs_uint32 f_fsid1; /* file system id */

                    afs_uint32 f_fsid2; /* file system id */

                    afs_uint32 f_namelen; /* maximum length of filenames */

                    afs_uint32 f_spare[6]; /* spare for later */

                };

struct mystat {

                    afs_uint32 st_dev; /* device */

                    afs_uint32 st_ino; /* inode */

                    afs_uint32 st_mode; /* protection */

                    afs_uint32 st_nlink; /* number of hard links */

                    afs_uint32 st_uid; /* user ID of owner */

                    afs_uint32 st_gid;/* group ID of owner */

                    afs_uint32 st_rdev; /* device type (if inode device) */

                    afs_uint32 st_size; /* total size, in bytes */

                    afs_uint32 st_blksize; /* blocksize for filesystem I/O */

                    afs_uint32 st_blocks; /* number of blocks allocated */

                    afs_uint32 st_atim; /* time of last access */

                    afs_uint32 st_mtim; /* time of last modification */

                    afs_uint32 st_ctim; /* time of last change */

                };

struct my_dirhandle{

                    afs_uint32 type;

                    afs_uint32 inode;

                    char name[MYMAXPATH];

                }

typedef my_dirhandle bulkmydirhandles<>;

 /*********************phase 1 functions *********************************************/

rxc_getattr(IN string mypath<MYMAXPATH>, IN int dummy) split = 1;

rxc_getdirWrapper(IN string path<MYMAXPATH>, OUT bulkmydirhandles *handles) = 2;

rxc_read(IN string path<MYMAXPATH>;, IN afs_uint32 size, IN afs_uint32 offset,

         IN struct my_file_info *fi) split = 3;

rxc_open(IN string path<MYMAXPATH>, IN int flags, OUT u_int *hd) = 4;

rxc_write(IN string path<MYMAXPATH>,IN afs_uint32 size, IN afs_uint32 offset,

          IN struct my_file_info *fi) split = 5;

rxc_chmod(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 6;

rxc_chown(IN string path<MYMAXPATH>, IN afs_uint32 uid, IN afs_uint32 gid) = 7;

rxc_utime(IN string path<MYMAXPATH>, IN afs_uint32 at,IN afs_uint32 mt) = 8;

rxc_mknod(IN string path<MYMAXPATH>, afs_uint32 mode, afs_uint32 rdev) = 9 ;

rxc_mkdir(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 10;

rxc_unlink(IN string path<MYMAXPATH>) = 11 ;

rxc_rmdir(IN string path<MYMAXPATH>) = 12;

rxc_rename(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 13;

rxc_truncate(IN string path<MYMAXPATH>, IN afs_uint32 size) = 14;

rxc_release(IN string path<MYMAXPATH>, IN struct my_file_info *fi) = 15;

rxc_readlink(IN string path<MYMAXPATH>, IN afs_uint32 size,OUT string

             data<MYMAXPATH>) = 16;

rxc_symlink(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 17;

rxc_link(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 18;

rxc_statfs(IN string path<MYMAXPATH>, OUT struct mystatfs *stbuf) = 19;

rxc_fsync(IN string path <MYMAXPAT>, IN int isdatasync, IN struct my_file_info

          *fi) = 20 ;

rxc_flush(IN string path <MYMAXPATH>, IN struct my_file_info *fi) = 21 ;

 

当要定义这个RX RPC 层的时候, 请注意下面的几点:

  • 你已经定义的 mystatfs, mystat, my_file_info 作为一个在 statfs, stat, fuse_file_info 机构体之上的包装器. 这些将是用生成的XDR 代码在网络上传输. (XDR, External Data Representation, 允许数据用一个构架-独立的风格被包装以便数据能在不同种类的计算机系统上传输.)
  • 你几乎已经为结构体fuse_operations 中的每一个成员定义了一个参数几乎相同的函数since the job of the 因为afsfuse_client 的工作只是从 FUSE 文件系统中取出调用并把他们传递到afsfuse_server.
  • 你已经采用了类似于MYMAXPATH的一些来自于系统的硬编码 在这里采用硬编码只是出于简单的理由.

 

 

创建客户端和桩文件

下一步是编译rxgen afsfuse.xg 文件去生成客户端和桩文件. 从包含afsfuse_server afsfuse_client的源代码的目录里, 运行命令 openafs-1.2.13/i386_linux24/dest/bin/rxgen afsfuse.xg. 这将生成下面的文件:

  • afsfuse.cs.c 是要和afsfuse_client.c被连接的客户端桩代码.
  • afsfuse.h 是包含你的FUSE RX代码的大量定义的头文件.
  • afsfuse.ss.c 是要和你的afsfuse_server代码进行连接的服务器桩代码(代理代码).
  • afsfuse.xdr.c 包含你在你的afsfuse.xg文件里定义的三个结构体的代码.

现在增加一些做实际工作的代码到afsfuse_client.c afsfuse_server.c 文件里. 几乎这些调用看起来都像下面那样:

  • Our_call_in_afs_fuse_client(). 包装(Marshall)参数并为RPC做准备. 调用RX [RPC] 之上的afsfuse_server 应用程序. 解包(Demarshall)参数. 拷贝值到这些函数里的正式的参数.
  • Our_call_in_afs_fuse_server().解包(Demarshall)参数. 调用本地文件系统或者AFS的特定函数. 包装参数并为RPC做准备. 做一个 RX RPC 调用.

afsfuse_client.c 调用看起来像下面那样:

int afsfuse_readlink(const char *path, char *buf, size_t size){

    rx_connection *local& int ret& char *buffer = malloc (512)&

    memset(buffer,0,512)& memset(buf,0,size)& local = getconnection()&

    ret = rxc_rxc_readlink(local,path,512,&buffer) // rpc call

         relconnection(local)&

         strncpy(buf,buffer,512-1)&

        //<- demarshall the parametrs

        return ret&

    }

 

afsfuse_server.c 的调用看起来像下面那样:

Listing 3. afsfuse_server.c 的调用

int rxc_rxc_readlink( struct rx_call *call, char * path, afs_uint32 size, char**data)

    { int ret& char lbuff[512] ={0}&

    translatePath(path,lbuff)& //<- make filesystem call

     *data = malloc(512)&

     res = readlink(lbuff, *data, 512-1)&

     if(res == -1) return -errno& (*data)[res] = '\0'& return 0&

    }

 

同样的, 你可以最佳一个代码到其他的函数里去加强你的文件系统.

你将需要创建一个makefile文件去编译你的代码. 记住当编译afsfuse_client的代码时要包含下面的选项: -D_FILE_OFFSET_BITS=64 and -DFUSE_USE_VERSION=22.


Listing 4.
生成makefile文件去编译客户端代码

SRCDIR=./ LIBRX=${SRCDIR}lib/librx.a

LIBS=${LIBRX} ${SRCDIR}lib/liblwp.a

#CC = g++

CFLAGS=-g -I. -I${SRCDIR}include -I${SRCDIR}include/fuse/ -DDEBUG ${XCFLAGS}

    -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=22

afsfuse_client: afsfuse_client.o afsfuse.xdr.o ${LIBS} bulk_io.o afsfuse.cs.o

    ${CC} ${CFLAGS} -o afsfuse_client afsfuse_client.o ${SRCDIR}lib/fuse/fuse.o

    ${SRCDIR}lib/fuse/mount.o ${SRCDIR}lib/fuse/helper.o

    ${SRCDIR}lib/fuse/fuse_mt.o bulk_io.o afsfuse.cs.o afsfuse.xdr.o ${LIBS}

afsfuse_server: afsfuse_server.o afsfuse.xdr.o afsfuse.ss.o bulk_io.o ${LIBS}

    ${CC} ${CFLAGS} -o afsfuse_server afsfuse_server.o bulk_io.o afsfuse.ss.o

    afsfuse.xdr.o ${LIBS}

#afsfuse_client.o: afsfuse.h

#afsfuse_server.o: afsfuse.h

bulk_io.o: ${CC} -c -g -I${SRCDIR}include bulk_io.c afsfuse.cs.c afsfuse.ss.c

    afsfuse.er.c afsfuse.h afsfuse.xdr.c: afsfuse.xg rxgen afsfuse.xg

afsfuse.xdr.o: afsfuse.xdr.c ${CC} -c -g -I{SRCDIR}include afsfuse.xdr.c

all: afsfuse_server afsfuse_client

clean: rm *.o rm afsfuse_client rm afsfuse_server

 

记住, 你将需要使用librx.a liblwp.a 去连接RX LWP 代码. 并且fuse/fuse.o, fuse/helper.o, fuse/mount.o 你需要连接的FUSE .

 

 

结论

本文里, 你已经学到了怎么去安装FUSE OpenAFS 并且怎么去使用它们去创建和定制你自己的用户空间文件系统, 一个在Linux里完全功能的, 稳定的文件系统而且不要求你去给你当前的内核去打补丁或者重新编译它-- 你甚至不用去了解内核的模块编. 你已经看见了在使能一个FUSE文件系统里的两个关键概念的细节: 怎么去安装和配置FUSE内核模块,以及怎么去撬动FUSE 库和 APIs的能量.

 

 

下载

Description

Name

Size

Download method

AFSFuse filesystem sample code

l-fuse.zip

9KB

HTTP

http://www-128.ibm.com/developerworks/linux/library/l-fuse/?ca=dgr-lnxw09FUSE

- 作者: colding 2006年08月24日, 星期四 17:52  回复(0) |  引用(0) 加入博采

用FUSE开发自己的文件系统一 [转]

FUSE开发自己的文件系统

不需要内核编程知识



Level: Introductory

Sumit Singh (sumising@in.ibm.com), Software Engineer, IBM

28 Feb 2006

Filesystem in Userspace (FUSE), 你能开发一个用户空间的文件系统框架,而不需要了解文件系统地内部或者学习内核模块编程的相关知识. 按照本文的例子, 一步一步的指导你去安装, 定制, 和使能FUSE AFS文件系统, 因此你能在Linux®的用户空间创建一个你自己的拥有完全机能的文件系统.

一个文件系统是一种存储和管理计算机文件和目录以及他们包含的数据的有效方法, 而且还能让你很容易得去找道河访问它们.如果你使用过计算机, 你应该用过不止一种文件系统. 一个文件系统能提供一些扩展能力. 它能在一个基本的文件系统上写一个包装器去管理它的数据,并且还能够体统一个加强的,功能丰富的文件系统 (例如cvsfs-fuse, 就是一个给CVS提供了一个文件系统接口, 还有Wayback 文件系统, 就是为了保存老的拷贝数据提供了一个备份机制).

在用户空间文件系统没有出现之前, 文件系统开发是内核开发者的一个工作. 创建一个文件系统则需要了解内核编程和内核技术的相关知识 (就像 vfs). 并且调试的时候还需要C C++ 专门技术. 然而其他的开发者需要操控一个文件系统 —— 只是加入一些个人的特征(例如增加历史纪录或者正向缓存forward-caching) 和改进.

FUSE简介

FUSE让你开发一个拥有简单API库的全功能文件系统, 并能让非特权用户访问, 而且提供了一个安全实现. 另外, 更妙的是, FUSE有一个经过验证的可跟踪的稳定版.

使用 FUSE, 你能开发一个连接到FUSE 库的文件系统就像一个可执行的二进制程序一样 -- 也就是说, 这个文件系统框架不要求你去学习额外的文件系统内部和内核模块的编程知识.

FUSE真正成为一个文件系统之前, 用户空间的文件系统并不是一个新的设计. 下面有些关于商业和学术上实现的用户空间的文件系统的例子:

  • LUFS是一个混血的用户空间文件系统构架,它为所有应用程序提供了数量不定的文件系统. 它分成内核模块和用户空间的后台程序两部分. 基本上它把大多数VFS层的调用委派给专门来处理这些的后台进程.
  • UserFS 允许用户进程作为一个正常的文件系统被挂载. 以这个proof-of-concept为原型开发了ftpfs, 一个允许匿名FTP的文件系统.
  • Ufo 工程是一个Solaris的全局文件系统,它允许用户把远程文件当作本地文件来使用.
  • OpenAFS是一个Andrew FileSystem的开源版本.
  • CIFS一个普通的网际文件系统.

不像这些商业和学术例子, FUSE把文件系统设计的功能带到Linux. 因为FUSE 一个可执行的程序(而不是说, 作为LUFS 使用的一个共享对象), 它让调试和开发变得容易起来. FUSE能工作在内核(2.4.x 2.6.x) 以及现在支持Java™ 绑定, 因此你不用局限于使用C C++语言来进行文件系统的编程. (参照Resources 了解更多的使用FUSE的用户空间的文件系统.)

如果要用FUSE来创建文件系统, 你需要安装一个FUSE 内核模块,然后使用FUSE 库和 API集来创建你的文件系统.


 

解压缩FUSE

要开发文件系统, 首先下载FUSE源代码 (参看 Resources) 后进行解压缩: tar -zxvf fuse-2.2.tar.gz. 它将创建一个存放源代码的FUSE 目录. fuse-2.2版本的目录内容如下:

  • ./doc 包含FUSE相关的文档. 这里仅仅只有一个文件, how-fuse-works.
  • ./kernel 包含FUSE 内核模块源代码 (当然, 你不需要知道怎么用FUSE来开发文件系统).
  • ./include包含FUSE 用来创建一个文件系统所需的API 头文件. 目前你仅仅需要一个头文件fuse.h.
  • ./lib 保持着用来创建FUSE 库的源代码,他们将用来连接你的二进制程序来创建一个文件系统.
  • ./util 含有FUSE 工具库的源代码.
  • ./example, 当然, 也包含一些可供你参考的例子, 例如fusexmp.null hello 文件系统.

 

 

构建和安装FUSE

1.       运行和配置来自fuse-2.2目录下的脚本: ./configure. 这将创建一个编译所需要的makefiles文件等.

2.       运行 ./make 去构建库, 二进制程序, 和内核模块. 检查kernel 目录下是否有./kernel/fuse.ko文件 这是一个内核模块文件. 另外还要检查 lib 目录是否包含fuse.o, mount.o, helper.o文件.

3.       执行 ./make install 去完成 FUSE的安装.

备注: 如果你想要手动的使用insmod安装这些模块到内核里的话,可以掠过这一步. 此时你可以使用: /usr/local/sbin/insmod ./kernel/fuse.ko 或者/sbin/insmod ./kernel/fuse.ko. 注意要安装需要的模块得需要root权限.

你可以用一条命令行来代替上面的步骤. fuse-2.2 目录下执行 ./configure; make; make install;既可.

重要提示: 当编译FUSE, 你需要有内核头文件或者内核源代码. 为了使事情变得简单, 必须保证内核源代码在 /usr/src/ 目录下.

 

定制文件系统

现在让我们创建一个文件系统以便你能使用一个老版本的Linux内核就可以访问Linux主机上的最新版本的内核的AFS 空间. 你将拥有两个进城: 一个是运行在老版本Linux内核上的服务器端进程, 另一个是运行在最新的Linux内核上的FUSE客户端进程. 无论什么时候一个请求来到你的FUSE 客户端进程的时候, 它将联系远程服务器端进程. 为了达到通信的目的, 这个文件系统使用AFS 的一部分RX RPC 代码, 因此你需要先构建OpenAFS. ( 1 AFS 文件系统的框图.)


1. AFS-FUSE文件系统概况

 

构建 OpenAFS

1.       下载OpenAFS Linux 源代码并解压.

进入到你解压源代码包后的目录里, 执行./make; ./configure --enable-transarc-paths. 如果 ./configure 不知道要构建的sysname, 然后使用--with-afs-sysname 选项指定一个合适的sysname.

为了在Linux 2.4 内核上构建文件系统, 请使用下面的命令: ./configure --enable-transarc-paths --with-afs-sysname=i386_linux24.

2.       执行./make, 然后执行 ./make dest. 检查再构建期间的任何错误.

如果编译没有什么问题的话, 那么你得AFS源代码树就可以准备工作了. 在这个阶段, 你需要准备一个名字叫afsfuse的开发目录. 在这个目录下, 生成下面两个目录:

o        include 目录将包含来自OpenAFS FUSE的头文件.

o        lib 目录将包含来自OpenAFS FUSE的库文件.

3.       拷贝头文件和库文件.

首先拷贝AFS 的头文件。把dest\i386_linux24\include下面的目录和文件拷贝到include 目录下. 然后从fuse-2.2目录下拷贝FUSE 的头文件到include 目录下. 重复以上的操作把库文件拷贝到lib目录里.

4.       创建应用程序的结构.

你需要为两组进程准备两套源代码文件. 使用afsfuse_client.*这样的名字来命名客户端进程;使用afsfuse_server.*这样的名字来命名服务器端的进程.

afsfuse_client.c 文件里包含了FUSE 进程的代码, afsfuse_server.c 文件里包含了运行在远程机器上的进程的服务器代码, 另外还有makefile文件, 和一个 为创建RPC(例如afsfuse.xg)而准备的 rxgen 文件.

这个afsfuse_client.c 文件将生成一个由FUSE 文件系统调用的afsfuse_client 进程代码来创建你属于自己的文件系统(请使用例子 fuse-2.2/example/fusexmp.c 来创建这个文件).

定义必要的函数

使用 FUSE来创建文件系统, 你需要声明一个fuse_operations类型的结构体变量并且把它传递到fuse_main函数里. 这个fuse_operations 结构体装载了指向为一系列特定动作而准备调用的函数表. Listing 1 显示了 fuse_operations 结构体的内容.


Listing 1. fuse_operation
结构体中必要的函数

struct fuse_operations {

    int (*getattr) (const char *, struct stat *);

    int (*readlink) (const char *, char *, size_t);

    int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);

    int (*mknod) (const char *, mode_t, dev_t);

    int (*mkdir) (const char *, mode_t);

    int (*unlink) (const char *);

    int (*rmdir) (const char *);

    int (*symlink) (const char *, const char *);

    int (*rename) (const char *, const char *);

    int (*link) (const char *, const char *);

    int (*chmod) (const char *, mode_t);

    int (*chown) (const char *, uid_t, gid_t);

    int (*truncate) (const char *, off_t);

    int (*utime) (const char *, struct utimbuf *);

    int (*open) (const char *, struct fuse_file_info *);

    int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);

    int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *);

    int (*statfs) (const char *, struct statfs *);

    int (*flush) (const char *, struct fuse_file_info *);

    int (*release) (const char *, struct fuse_file_info *);

    int (*fsync) (const char *, int, struct fuse_file_info *);

    int (*setxattr) (const char *, const char *, const char *, size_t, int);

    int (*getxattr) (const char *, const char *, char *, size_t);

    int (*listxattr) (const char *, char *, size_t);

    int (*removexattr) (const char *, const char *);

};

其中没有一个函数是绝对必要的, 但是它们当中的很多函数也需要为文件系统适当的工作. 你能用特定目标的方法(.flush, .release, 或者 .fsync)来实现一个完全特征的文件系统. (本文不涉及到了任何xattr 函数.) Listing 1中的这些函数在下面有其说:

  • getattr: int (*getattr) (const char *, struct stat *);
    stat()相似的一个函数. st_dev st_blksize 域被忽略了.除非use_ino挂载选项被指定 st_ino 域也将被忽略.
  • readlink: int (*readlink) (const char *, char *, size_t);
    读一个符号连接的目标文件. 该函数将会用一个有终结符的字符串来填充缓冲区. 这个缓冲区的 size 参数也要包含一个终结符. 如果linkname 太长以至于不能填充到这个缓冲区里的话, 它将被截断. 这个函数的返回值在成功时返回"0".
  • getdir: int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
    读一个目录的内容. 它是 opendir(), readdir(), ..., closedir() 这一系列操作中的一个调用. 为了把每一个目录entry填充到特定的缓冲区里, filldir() 函数将会被调用.
  • mknod: int (*mknod) (const char *, mode_t, dev_t);
    创建一个文件的索引节点. 它不是一个 create() 操作; mknod() 将被用于创建所有的非目录,非符号连接的索引节点.
  • mkdir: int (*mkdir) (const char *, mode_t);
    rmdir: int (*rmdir) (const char *);
    分别是创建和删除一个目录.
  • unlink: int (*unlink) (const char *);
    rename: int (*rename) (const char *, const char *);
    分别时删除和重命名一个文件.
  • symlink: int (*symlink) (const char *, const char *);
    创建一个符号连接.
  • link: int (*link) (const char *, const char *);
    给指定的文件创建一个硬连接.
  • chmod: int (*chmod) (const char *, mode_t);
    chown: int (*chown) (const char *, uid_t, gid_t);
    truncate: int (*truncate) (const char *, off_t);
    utime: int (*utime) (const char *, struct utimbuf *);
    它们分别是改变的文件的权限位, 拥有者和属组, 大小, 以及访问/更改时间.
  • open: int (*open) (const char *, struct fuse_file_info *);
    打开一个文件. 不需要把创建或者截断标志(O_CREAT, O_EXCL, O_TRUNC) 传递到 open(). 它将检查该操作根据制定的标志符是否被允许. 另外, open() 也将返回一个文件描述符到defanged-span lang="EN-US">fuse_file_info 结构体里.
  • read: int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
    从一个被打开的文件里读数据. read() 将准确的返回读出数据的字节数, 但是除了遇到文件尾或者一个错误; 否则, 其余的数据将被用0来取代. direct_io 挂载选项被指定时将返回一个异常, 而将用read() 系统调用的返回值代替本函数的返回值.
  • write: int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *);
    写数据到打开的文件里. write() 将准确的返回写进文件里的数据的字节数除非遇到一个错误. direct_io 挂载选项被指定时也会抛出一个异常 ( read() 函数一样).
  • statfs: int (*statfs) (const char *, struct statfs *);
    取得文件系统的统计数据. f_type f_fsid 域将被忽略.
  • flush: int (*flush) (const char *, struct fuse_file_info *);
    将把缓存中的数据刷新到磁盘上. 它和 fsync() 函数不一样 - 不会同步脏数据. close() 关闭文件描述符时会调用flush() 函数, 因此如果一个文件系统想要在close()期间返回写错误并且这个文件已经缓存了一些脏数据, 这是一个写回数据并返回一些错误的好地方. 而许多的应用程序都忽略close() 的错误, 它不总是有用的.

注意: flush() 方法对于每个open()可能被调用不止一次. 如果文件通过dup(), dup2(), 或者fork() 系统调用被打开它将会发生很多次. 还不能确定一个刷新是否是最终的, 因此每一个刷新都将被公平的被对待. 多次的写-刷新序列是相对的少见, 所以这将不是一个问题.

  • release: int (*release) (const char *, struct fuse_file_info *);
    释放一个被打开的文件. 当他们不在引用一个打开的文件时release() 被调用-- 所有的文件描述符都被关闭后并且所有的内存映射都被解除映射后. 对于每一个 open() 调用, 都将是一个带有同样标志位和文件描述符的release()defanged-span lang="EN-US"> 调用. 一个文件很可能被打开不止一次, 而在这种情况下仅仅是最后一次释放操作将被统计并且不在对文件进行读写. 释放操作的返回值将被忽略.
  • fsync: int (*fsync) (const char *, int, struct fuse_file_info *);
    同步文件的内容. 如果 datasync 参数是非零值, 那么仅仅是用户数据将被刷新到磁盘上, 而不是元数据.
  • setxattr: int (*setxattr) (const char *, const char *, const char *, size_t, int);
    getxattr: int (*getxattr) (const char *, const char *, char *, size_t);
    listxattr: int (*listxattr) (const char *, char *, size_t);
    removexattr: int (*removexattr) (const char *, const char *);
    这些分别是设置, 取得, 列出, 并且删除文件的扩展属性.

最终的文件系统

你的文件系统看起来有下面的一些东西:

afsfuse_client   <--RX[RPC]-->   afsfuse_server

afsfuse_client 经运送文件系统的调用到驻留在远程机器上的afsfuse_server. afsfuse_server 将为所有的来自于客户端的请求提供服务并且返回结果给客户端. 它将作所有的必要的工作. RPC提供的机制是RX. 不为用户数据或者是元数据提供缓存.

- 作者: colding 2006年08月24日, 星期四 17:51  回复(0) |  引用(0) 加入博采

如何将整型数转成字符型?
方法一:
int buf[100]; //1 原始数据

char * p = (char *)buf; //2 用字节指针访问这段内存

//假设串口发送函数的原型
// char *p; 发送缓冲区
// len 要发送的字节数
//int sendbycomm(char * pbuf, int len);

int nSend = sendbycomm(p,sizeof[buf]); //3 发送

assert(nSend == sizeof[buf]); //如果没有错误的话,nSend == sizeof[buf]

方法二:
sprintf() 函数。
char cur_date[20];
         sprintf(cur_date, "%d%02d%02d",cur_time->tm_year+1900,cur_time->tm_mon+1,cur_time->tm_mday);

- 作者: colding 2006年08月9日, 星期三 17:07  回复(0) |  引用(0) 加入博采

Unix 高级用户命令 lsof
  在unix/linux下运行程序的时候需要知道当前进程处理的文件,lsof命令可以查看当前所有进程打开的所有文件。

Overview

LiSt Open Files is a useful and powerful tool that will show you opened files. In Unix everything is a file: pipes are files, IP sockets are files, unix sockets are files, directories are files, devices are files, inodes are files...

Usful Examples

So in this tangle of files lsof listst files opened by processes running on your system.

When lsof is called without parameters, it will show all the files opened by any processes.

     lsof | nl

Let us know who is using the apache executable file, /etc/passwd, what files are opened on device /dev/hda6 or who's accessing /dev/cdrom:

     lsof `which apache2`
     lsof /etc/passwd
     lsof /dev/hda6
     lsof /dev/cdrom

Now show us what process IDs are using the apache binary, and only the PID:

     lsof -t `which apache2`

Show us what files are opened by processes whose names starts by "k" (klogd, kswapd...) and bash. Show us what files are opened by init:

     lsof -c k
     lsof -c bash
     lsof -c init

Show us what files are opened by processes whose names starts by "courier", but exclude those whose owner is the user "zahn":

     lsof -c courier -u ^zahn

Show us the processes opened by user apache and user zahn:

     lsof -u apache,zahn

Show us what files are using the process whose PID is 30297:

     lsof +p 30297

Search for all opened instances of directory /tmp and all the files and directories it contains:

     lsof +D /tmp

List all opened internet sockets and sockets related to port 80:

     lsof -i
     lsof -i :80

List all opened Internet and UNIX domain files:

     lsof -i -U

Show us what process(es) has an UDP connection opened to or from the host www.akadia.com at port 123 (ntp):

     lsof -iUDP@www.akadia.com:123

lsof provides many more options and could be an unvaluable foresinc tool if your system get compromised or as daily basis check tool.


- 作者: colding 2006年08月8日, 星期二 16:28  回复(0) |  引用(0) 加入博采

Bourne Shell (一) [转]

Bourne Shell (一)

                                      

Bourne Shell

介绍:Bourne Shell 基础及其他很多有用的特性,shell编程及组织。

主要内容:
.shell基础 基本介绍,环境,选项,特殊字符
.shell变量 用户定义变量,环境变量,位置变量(shell 参数)
.shell script编程
条件测试,循环及重复控制
.shell定制

1.shell基础知识
作者:Stephen Bourne 在Bell实验室开发
建议:man sh 查看相关UNIX上的改进或特性

(1)shell提示符及其环境
/etc/passwd文件
提示符:$
/etc/profile $HOME/.profile
(2)shell执行选项
-n 测试shell script语法结构,只读取shell script但不执行
-x 进入跟踪方式,显示所执行的每一条命令,用于调度
-a Tag all variables for export
-c "string" 从strings中读取命令
-e 非交互方式
-f 关闭shell文件名产生功能
-h locate and remember functions as defind
-i 交互方式
-k 从环境变量中读取命令的参数
-r 限制方式
-s 从标准输入读取命令
-t 执行命令后退出(shell exits)
-u 在替换中如使用未定义变量为错误
-v verbose,显示shell输入行

这些选项可以联合使用,但有些显然相互冲突,如-e和-i.

(3)受限制shell(Restircted Shell)
sh -r 或 /bin/rsh

不能执行如下操作:cd, 更改PATH,指定全路径名,输出重定向,因此可以提供一个较
好的控制和安全机制。通常rsh用于应用型用户及拨号用户,这些用户通常是看不到提
示符的。通常受限制用户的主目录是不可写的。

不足:如果用户可以调用sh,则rsh的限制将不在起作用,事实上如果用户在vi及more
程序中调用shell,而这时rsh的限制将不再起作用。

(4)用set改变 shell选项
用户可以在$提示符下用set命令来设置或取消shell的选项。使用-设置选项,+取消相应
选项,大多数UNIX系统允许a,e,f,h,k,n,u,v和x的开关设置/取消。

set -xv
启动跟踪方式;显示所有的命令及替换,同样显示输入。
set -tu
关闭在替换时对未定义变量的检查。

使用echo $-显示所有已设置的shell选项。


(5)用户启动文件 .profile
PATH=$PATH:/usr/loacl/bin; export PATH

(6)shell环境变量
CDPATH 用于cd命令的查找路径
HOME /etc/passwd文件中列出的用户主目录
IFS Internal Field Separator,默认为空格,tab及换行符
MAIL /var/mail/$USERNAME mail等程序使用
PATH
PS1,PS2 默认提示符($)及换行提示符(>)
TERM 终端类型,常用的有vt100,ansi,vt200,xterm等

示例:$PS1="test:";export PS1
test: PS1="$";export PS1
$echo $MAIL
/var/mail/username
(7)保留字符及其含义
$ shell变量名的开始,如$var
| 管道,将标准输出转到下一个命令的标准输入
# 注释开始
& 在后台执行一个进程
? 匹配一个字符
* 匹配0到多个字符(与DOS不同,可在文件名中间使用,并且含.)
$- 使用set及执行时传递给shell的标志位
$! 最后一个子进程的进程号
$# 传递给shell script的参数个数
$* 传递给shell script的参数
$@ 所有参数,个别的用双引号括起来
$? 上一个命令的返回代码

 
Borne Shell

介绍:Bourne Shell 基础及其他很多有用的特性,shell编程及组织。

主要内容:
.shell基础 基本介绍,环境,选项,特殊字符
.shell变量 用户定义变量,环境变量,位置变量(shell 参数)
.shell script编程
条件测试,循环及重复控制
.shell定制

1.shell基础知识
作者:Stephen Bourne 在Bell实验室开发
建议:man sh 查看相关UNIX上的改进或特性

(1)shell提示符及其环境
/etc/passwd文件
提示符:$
/etc/profile $HOME/.profile
(2)shell执行选项
-n 测试shell script语法结构,只读取shell script但不执行
-x 进入跟踪方式,显示所执行的每一条命令,用于调度
-a Tag all variables for export
-c "string" 从strings中读取命令
-e 非交互方式
-f 关闭shell文件名产生功能
-h locate and remember functions as defind
-i 交互方式
-k 从环境变量中读取命令的参数
-r 限制方式
-s 从标准输入读取命令
-t 执行命令后退出(shell exits)
-u 在替换中如使用未定义变量为错误
-v verbose,显示shell输入行

这些选项可以联合使用,但有些显然相互冲突,如-e和-i.

(3)受限制shell(Restircted Shell)
sh -r 或 /bin/rsh

不能执行如下操作:cd, 更改PATH,指定全路径名,输出重定向,因此可以提供一个较
好的控制和安全机制。通常rsh用于应用型用户及拨号用户,这些用户通常是看不到提
示符的。通常受限制用户的主目录是不可写的。

不足:如果用户可以调用sh,则rsh的限制将不在起作用,事实上如果用户在vi及more
程序中调用shell,而这时rsh的限制将不再起作用。

(4)用set改变 shell选项
用户可以在$提示符下用set命令来设置或取消shell的选项。使用-设置选项,+取消相应
选项,大多数UNIX系统允许a,e,f,h,k,n,u,v和x的开关设置/取消。

set -xv
启动跟踪方式;显示所有的命令及替换,同样显示输入。
set -tu
关闭在替换时对未定义变量的检查。

使用echo $-显示所有已设置的shell选项。


(5)用户启动文件 .profile
PATH=$PATH:/usr/loacl/bin; export PATH

(6)shell环境变量
CDPATH 用于cd命令的查找路径
HOME /etc/passwd文件中列出的用户主目录
IFS Internal Field Separator,默认为空格,tab及换行符
MAIL /var/mail/$USERNAME mail等程序使用
PATH
PS1,PS2 默认提示符($)及换行提示符(>)
TERM 终端类型,常用的有vt100,ansi,vt200,xterm等

示例:$PS1="test:";export PS1
test: PS1="$";export PS1
$echo $MAIL
/var/mail/username
(7)保留字符及其含义
$ shell变量名的开始,如$var
| 管道,将标准输出转到下一个命令的标准输入
# 注释开始
& 在后台执行一个进程
? 匹配一个字符
* 匹配0到多个字符(与DOS不同,可在文件名中间使用,并且含.)
$- 使用set及执行时传递给shell的标志位
$! 最后一个子进程的进程号
$# 传递给shell script的参数个数
$* 传递给shell script的参数
$@ 所有参数,个别的用双引号括起来
$? 上一个命令的返回代码
当前shell的名字
$n (n:1-) 位置参数
$$ 进程标识号(Process Identifier Number, PID)
>file 输出重定向
`command` 命令替换,如 filename=`basename /usr/local/bin/tcsh`
>>fiile 输出重定向,append

转义符及单引号:
$echo "$HOME $PATH"
/home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:
$echo '$HOME $PATH'
$HOME $PATH
$echo $HOME $PATH
$HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbwork/bin

其他:
$dir=ls
$$dir
$alias dir ls
$dir

ls > filelist
ls >> filelist
wc -l < filelist
wc -l filelist
sleep 5; echo 5 seconds reaches; ls -l
ps ax |egrep inetd
find / -name core -exec rm {} \; &
filename=`date "+%Y%m%d"`.log

2. shell变量
变量:代表某些值的符号,如$HOME,cd命令查找$HOME,在计算机语言中可以使用变量可以
进行多种运算和控制。

Bourne Shell有如下四种变量:
.用户自定义变量
.位置变量即 shell script之参数
.预定义变量(特殊变量)
.环境变量(参考shell定制部分)
(1)用户自定义变量(数据的存储)
$ COUNT=1
$ NAME="He Binwu"

技巧:因为大部分UNIX命令使用小写字符,因此在shell编程中通常使用全大写变量,
当然这并不是强制性的,但使用大写字符可以在编程中方便地识别变量。

变量的调用:在变量前加$
$ echo $HOME
/home/hbwork
$ WEEK=Satur
$ echo Today is $WEEKday
Today is
$echo Today is ${WEEK}day
Today is Saturday

Shell变量赋值从右从左进行(Linux Shell/bash从左向右赋值!)
$ X=$Y Y=y
$ echo $X
y
$ Z=z Y=$Z
$ echo $Y

$

使用unset命令删除变量的赋值
$ Z=hello
$ echo $Z
hello
$ unset Z
$ echo $Z

$

有条件的命令替换
在Bourne Shell中可以使变量替换在特定条件下执行,即有条件的环境变量替换。
这种变量替换总是用大括号括起来的。

.设置变量的默认值
在变量未赋值之前其值为空。Bourne Shell允许对变量设置默认值,其格式如下:
${variable:-defaultvalue}
例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:-there}
Hello there
$ echo $UNAME #变量值并未发生变化

$ UNAME=hbwork
$ echo Hello ${UNAME:-there}
Hello hbwork
$
.另一种情况:改变变量的值,格式如下:
${variable:=value}

例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:=there}
Hello there
$ echo $UNAME #变量值并未发生变化
there
$
.变量替换中使用命令替换
$USERDIR=${$MYDIR:-`pwd`}

.在变量已赋值时进行替换 ${variable:+value}
.带有错误检查的有条件变量替换
${variable:?value}
例:
$ UNAME=
$ echo ${UNAME:?"UNAME has not been set"}
UNAME: UNAME has not been set
$ echo ${UNAME:?}
UNAME: parameter null or not set

(2)位置变量(Shell参数)
在shell script中位置参数可用..表示,表示内容通常为当前执行程序的文件名。
.防止变量值被替换 readonly variable
.使用export命令输出变量,使得变量对子shell可用,当shell执行一下程序时,shell
将为其设置一个新的环境让其执行,这称之了subshell. 在Bourne Shell中变量通常
被认为是本地变量,也就是说在对其赋值之外的shell环境之外是不认识此变量的。使
用export对subshell可用。

例:对变量PS1的export操作,shell的提示符将发生变化。
$ PS1=`hostname`$
peony$sh
$ echo $PS1
$ <-输出结果
$ exit
peony$export PS1
peony$sh
peony$ echo $PS1
peony$ <-输出结果
peony$


3.Shell Script编程
目的:使用UNIX所提供的最常用工具来完成所需复杂任务的强大功能。

(1)最简单的Shell 编程
$ls -R / |grep myname |more

每天数据的备份:
$ cd /usr/yourname; ls * |cpio -o > /dev/rmt/0h

书写程序的目的是一次编程,多次使用(执行)!

$ cat > backup.sh
cd /home/hbwork
ls * | cpio -o > /dev/rmt/0h
^D

执行:
$ sh backup.sh

或:
$ chmod +x backup.sh
$ ./backup.sh

技巧:在shell script中加入必要的注释,以便以后阅读及维护。

(2)shell是一个(编程)语言
同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script
编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子
程序及以中断处理等。

. 在shell编程中使用数据变量可以将程序变量更为通用,如在上面backup.sh中:
cd $WORKDIR
ls * | cpio -o > /dev/rmt/0h

. Shell编程中的注释以#开头
. 对shell变量进行数字运算,使用expr命令
expr integer operator integer
其中operator为+ - * / %, 但对*的使用要用转义符\,如:
$expr 4 \* 5
20
$int=`expr 5 + 7`
$echo $int
12

(3)Shell编程的参数传递, 可通过命令行参数以及交互式输入变量(read)

restoreall.sh 对backup.sh程序的备份磁带进行恢复
$cat > restoreall.sh
cd $WORKDIR
cpio -i < /dev/rmt/0h
^D
restore1.sh:只能恢复一个文件
#restore1 --program to restore a single file
cd $WORKDIR
cpio -i $ < /dev/rmt/0h

$restore1 file1

恢复多个文件restoreany :
#restoreany --program to restore a single file
cd $WORKDIR
cpio -i $* < /dev/rmt/0h

$ restoreany file1 file2 file3


(4)条件判断
. if-then语句,格式如下:
if command_1
then
command_2
command_3
fi
command_4

在if-then语句中使用了命令返回码$?,即当command_1执行成功时才执行command_2和
command_3,而command_4总是执行.

示例程序unload: 在备份成功时删除原始文件,带有错误检查

cd
#备份时未考虑不成功的情况!
ls -a | cpio -o > /dev/rmt/0h
rm -rf *

改进如下:

#当使用了管道命令时,管理命令的返回代码为最后一个命令的返回代码
if ls -a | cpio -o > /dev/rmt/0h
then
rm -rf *
fi

. if-then-else语句
if command_1
then
command_2
else
command_3
fi

技巧: 由于shell对命令中的多余的空格不作任何处理,一个好的程序员会用这一特性
对自己的程序采用统一的缩进格式,以增强自己程序的可读性.

. 使用test命令进行进行条件测试
格式: test conditions

test在以下四种情况下使用: a. 字符比较 b.两个整数值的比较
c. 文件操作,如文件是否存在及文件的状态等
d. 逻辑操作,可以进行and/or,与其他条件联合使用

a. 测试字符数据: shell变量通常民政部下均作为字符变量
str1 = str2 二者相长,相同
str1 != str2 不同
-n string string不为空(长度不为零)
-z string string为空
string string不为空

例:
$ str1=abcd #在含有空格时必须用引号括起来
$ test $str1=abcd
$ echo $?
0
$ str1="abcd "
$ test $str1=abcd
$ echo $?
1
Note: 在test处理含有空格的变量时最好用引号将变量括起来,否则会出现错误的结果,
因为shell在处理命令行时将会去掉多余的空格,而用引号括起来则可以防止
shell去掉这些空格.
例:
$ str1=" "
$ test $str1
$ echo $?
1
$ test "$str1"
$ echo $?
0
$ test -n $str1
test: argument expected
$ test -n "$str1"
$ echo $?
0
$

b. 整数测试: test与expr相同,可以将字符型变量转换为整数进行操作,expr进行
整数的算术运算,而test则进行逻辑运算.

表达式 说明
---------------------------------------
int1 -eq int2 相等?
int1 -ne int2 不等?
int1 -gt int2 int1 > int2 ?
int1 -ge int2 int1 >= int2 ?
int1 -lt int2 int1 < int2 ?
int1 -le int2 int1 <= int2 ?

例:
$ int1=1234
$ int2=01234
$ test $int1 -eq $int2
$ echo $?
0

c. 文件测试:检查文件状态如存在及读写权限等

-r filename 用户对文件filename有读权限?
-w filename 用户对文件filename有写权限?
-x filename 用户对文件filename有可执行权限?
-f filename 文件filename为普通文件?
-d filename 文件filename为目录?
-c filename 文件filename为字符设备文件?
-b filename 文件filename为块设备文件?
-s filename 文件filename大小不为零?
-t fnumb 与文件描述符fnumb(默认值为1)相关的设备是一个终端设备?

d. 测试条件之否定,使用!
例:
$ cat /dev/null > empty
$ test -r empty
$ echo $?
0
$ test -s empty
1
$ test ! -s empty
$ echo $?
0
e. 测试条件之逻辑运算
-a And
-o Or

例: $ test -r empty -a -s empty
$ echo $?
1
f. 进行test测试的标准方法
因为test命令在 shell编程中占有很重要的地位,为了使shell能同其他编程语言一样
便于阅读和组织, Bourne Shell在使用test测试时使用了另一种方法:用方括号将整个
test测试括起来:

$ int1=4
$ [ $int1 -gt 2 ]
$ echo $?
0

例: 重写unload程序,使用test测试
#!/bin/sh
#unload - program to backup and remove files
#syntax: unload directory

#check arguments
if [ $# -ne 1 ]
then
echo "usage: directory"
exit 1
fi

#check for valid directory name
if [ ! -d "" ]
then
echo " is not a directory"
exit 2
fi

cd

ls -a | cpio -o > /dev/rmt/0h

if [ $? -eq 0 ]
then
rm -rf *
else
echo "A problem has occured in creating backup"
echo "The directory will not be ereased"
echo "Please check the backup device"
exit 3
fi
# end of unload

在如上示例中出现了exit, exit有两个作用:一是停止程序中其他命令的执行,二是
设置程序的退出状态

g. if嵌套及elif结构
if command
then
command
else
if command
then
command
else
if command
then
command
fi
fi
fi

改进:使用elif结构
if command
then
command
elif command
then
command
elif command
then
command
fi

elif结构同if结构类似,但结构更清淅,其执行结果完全相同.


-------------未完,待续----------



当前shell的名字
$n (n:1-) 位置参数
$$ 进程标识号(Process Identifier Number, PID)
>file 输出重定向
`command` 命令替换,如 filename=`basename /usr/local/bin/tcsh`
>>fiile 输出重定向,append

转义符及单引号:
$echo "$HOME $PATH"
/home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:
$echo '$HOME $PATH'
$HOME $PATH
$echo $HOME $PATH
$HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbwork/bin

其他:
$dir=ls
$$dir
$alias dir ls
$dir

ls > filelist
ls >> filelist
wc -l < filelist
wc -l filelist
sleep 5; echo 5 seconds reaches; ls -l
ps ax |egrep inetd
find / -name core -exec rm {} \; &
filename=`date "+%Y%m%d"`.log

2. shell变量
变量:代表某些值的符号,如$HOME,cd命令查找$HOME,在计算机语言中可以使用变量可以
进行多种运算和控制。

Bourne Shell有如下四种变量:
.用户自定义变量
.位置变量即 shell script之参数
.预定义变量(特殊变量)
.环境变量(参考shell定制部分)
(1)用户自定义变量(数据的存储)
$ COUNT=1
$ NAME="He Binwu"

技巧:因为大部分UNIX命令使用小写字符,因此在shell编程中通常使用全大写变量,
当然这并不是强制性的,但使用大写字符可以在编程中方便地识别变量。

变量的调用:在变量前加$
$ echo $HOME
/home/hbwork
$ WEEK=Satur
$ echo Today is $WEEKday
Today is
$echo Today is ${WEEK}day
Today is Saturday

Shell变量赋值从右从左进行(Linux Shell/bash从左向右赋值!)
$ X=$Y Y=y
$ echo $X
y
$ Z=z Y=$Z
$ echo $Y

$

使用unset命令删除变量的赋值
$ Z=hello
$ echo $Z
hello
$ unset Z
$ echo $Z

$

有条件的命令替换
在Bourne Shell中可以使变量替换在特定条件下执行,即有条件的环境变量替换。
这种变量替换总是用大括号括起来的。

.设置变量的默认值
在变量未赋值之前其值为空。Bourne Shell允许对变量设置默认值,其格式如下:
${variable:-defaultvalue}
例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:-there}
Hello there
$ echo $UNAME #变量值并未发生变化

$ UNAME=hbwork
$ echo Hello ${UNAME:-there}
Hello hbwork
$
.另一种情况:改变变量的值,格式如下:
${variable:=value}

例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:=there}
Hello there
$ echo $UNAME #变量值并未发生变化
there
$
.变量替换中使用命令替换
$USERDIR=${$MYDIR:-`pwd`}

.在变量已赋值时进行替换 ${variable:+value}
.带有错误检查的有条件变量替换
${variable:?value}
例:
$ UNAME=
$ echo ${UNAME:?"UNAME has not been set"}
UNAME: UNAME has not been set
$ echo ${UNAME:?}
UNAME: parameter null or not set
(2)位置变量(Shell参数)
在shell script中位置参数可用..表示,
 
Bourne Shell

介绍:Bourne Shell 基础及其他很多有用的特性,shell编程及组织。

主要内容:
.shell基础 基本介绍,环境,选项,特殊字符
.shell变量 用户定义变量,环境变量,位置变量(shell 参数)
.shell script编程
条件测试,循环及重复控制
.shell定制

1.shell基础知识
作者:Stephen Bourne 在Bell实验室开发
建议:man sh 查看相关UNIX上的改进或特性

(1)shell提示符及其环境
/etc/passwd文件
提示符:$
/etc/profile $HOME/.profile
(2)shell执行选项
-n 测试shell script语法结构,只读取shell script但不执行
-x 进入跟踪方式,显示所执行的每一条命令,用于调度
-a Tag all variables for export
-c "string" 从strings中读取命令
-e 非交互方式
-f 关闭shell文件名产生功能
-h locate and remember functions as defind
-i 交互方式
-k 从环境变量中读取命令的参数
-r 限制方式
-s 从标准输入读取命令
-t 执行命令后退出(shell exits)
-u 在替换中如使用未定义变量为错误
-v verbose,显示shell输入行

这些选项可以联合使用,但有些显然相互冲突,如-e和-i.

(3)受限制shell(Restircted Shell)
sh -r 或 /bin/rsh

不能执行如下操作:cd, 更改PATH,指定全路径名,输出重定向,因此可以提供一个较
好的控制和安全机制。通常rsh用于应用型用户及拨号用户,这些用户通常是看不到提
示符的。通常受限制用户的主目录是不可写的。

不足:如果用户可以调用sh,则rsh的限制将不在起作用,事实上如果用户在vi及more
程序中调用shell,而这时rsh的限制将不再起作用。

(4)用set改变 shell选项
用户可以在$提示符下用set命令来设置或取消shell的选项。使用-设置选项,+取消相应
选项,大多数UNIX系统允许a,e,f,h,k,n,u,v和x的开关设置/取消。

set -xv
启动跟踪方式;显示所有的命令及替换,同样显示输入。
set -tu
关闭在替换时对未定义变量的检查。

使用echo $-显示所有已设置的shell选项。


(5)用户启动文件 .profile
PATH=$PATH:/usr/loacl/bin; export PATH

(6)shell环境变量
CDPATH 用于cd命令的查找路径
HOME /etc/passwd文件中列出的用户主目录
IFS Internal Field Separator,默认为空格,tab及换行符
MAIL /var/mail/$USERNAME mail等程序使用
PATH
PS1,PS2 默认提示符($)及换行提示符(>)
TERM 终端类型,常用的有vt100,ansi,vt200,xterm等

示例:$PS1="test:";export PS1
test: PS1="$";export PS1
$echo $MAIL
/var/mail/username
(7)保留字符及其含义
$ shell变量名的开始,如$var
| 管道,将标准输出转到下一个命令的标准输入
# 注释开始
& 在后台执行一个进程
? 匹配一个字符
* 匹配0到多个字符(与DOS不同,可在文件名中间使用,并且含.)
$- 使用set及执行时传递给shell的标志位
$! 最后一个子进程的进程号
$# 传递给shell script的参数个数
$* 传递给shell script的参数
$@ 所有参数,个别的用双引号括起来
$? 上一个命令的返回代码
当前shell的名字
$n (n:1-) 位置参数
$$ 进程标识号(Process Identifier Number, PID)
>file 输出重定向
`command` 命令替换,如 filename=`basename /usr/local/bin/tcsh`
>>fiile 输出重定向,append

转义符及单引号:
$echo "$HOME $PATH"
/home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:
$echo '$HOME $PATH'
$HOME $PATH
$echo $HOME $PATH
$HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbwork/bin

其他:
$dir=ls
$$dir
$alias dir ls
$dir

ls > filelist
ls >> filelist
wc -l < filelist
wc -l filelist
sleep 5; echo 5 seconds reaches; ls -l
ps ax |egrep inetd
find / -name core -exec rm {} \; &
filename=`date "+%Y%m%d"`.log

2. shell变量
变量:代表某些值的符号,如$HOME,cd命令查找$HOME,在计算机语言中可以使用变量可以
进行多种运算和控制。

Bourne Shell有如下四种变量:
.用户自定义变量
.位置变量即 shell script之参数
.预定义变量(特殊变量)
.环境变量(参考shell定制部分)
(1)用户自定义变量(数据的存储)
$ COUNT=1
$ NAME="He Binwu"

技巧:因为大部分UNIX命令使用小写字符,因此在shell编程中通常使用全大写变量,
当然这并不是强制性的,但使用大写字符可以在编程中方便地识别变量。

变量的调用:在变量前加$
$ echo $HOME
/home/hbwork
$ WEEK=Satur
$ echo Today is $WEEKday
Today is
$echo Today is ${WEEK}day
Today is Saturday

Shell变量赋值从右从左进行(Linux Shell/bash从左向右赋值!)
$ X=$Y Y=y
$ echo $X
y
$ Z=z Y=$Z
$ echo $Y

$

使用unset命令删除变量的赋值
$ Z=hello
$ echo $Z
hello
$ unset Z
$ echo $Z

$

有条件的命令替换
在Bourne Shell中可以使变量替换在特定条件下执行,即有条件的环境变量替换。
这种变量替换总是用大括号括起来的。

.设置变量的默认值
在变量未赋值之前其值为空。Bourne Shell允许对变量设置默认值,其格式如下:
${variable:-defaultvalue}
例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:-there}
Hello there
$ echo $UNAME #变量值并未发生变化

$ UNAME=hbwork
$ echo Hello ${UNAME:-there}
Hello hbwork
$
.另一种情况:改变变量的值,格式如下:
${variable:=value}

例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:=there}
Hello there
$ echo $UNAME #变量值并未发生变化
there
$
.变量替换中使用命令替换
$USERDIR=${$MYDIR:-`pwd`}

.在变量已赋值时进行替换 ${variable:+value}
.带有错误检查的有条件变量替换
${variable:?value}
例:
$ UNAME=
$ echo ${UNAME:?"UNAME has not been set"}
UNAME: UNAME has not been set
$ echo ${UNAME:?}
UNAME: parameter null or not set

(2)位置变量(Shell参数)
在shell script中位置参数可用..表示,表示内容通常为当前执行程序的文件名。
.防止变量值被替换 readonly variable
.使用export命令输出变量,使得变量对子shell可用,当shell执行一下程序时,shell
将为其设置一个新的环境让其执行,这称之了subshell. 在Bourne Shell中变量通常
被认为是本地变量,也就是说在对其赋值之外的shell环境之外是不认识此变量的。使
用export对subshell可用。

例:对变量PS1的export操作,shell的提示符将发生变化。
$ PS1=`hostname`$
peony$sh
$ echo $PS1
$ <-输出结果
$ exit
peony$export PS1
peony$sh
peony$ echo $PS1
peony$ <-输出结果
peony$


3.Shell Script编程
目的:使用UNIX所提供的最常用工具来完成所需复杂任务的强大功能。

(1)最简单的Shell 编程
$ls -R / |grep myname |more

每天数据的备份:
$ cd /usr/yourname; ls * |cpio -o > /dev/rmt/0h

书写程序的目的是一次编程,多次使用(执行)!

$ cat > backup.sh
cd /home/hbwork
ls * | cpio -o > /dev/rmt/0h
^D

执行:
$ sh backup.sh

或:
$ chmod +x backup.sh
$ ./backup.sh

技巧:在shell script中加入必要的注释,以便以后阅读及维护。

(2)shell是一个(编程)语言
同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script
编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子
程序及以中断处理等。

. 在shell编程中使用数据变量可以将程序变量更为通用,如在上面backup.sh中:
cd $WORKDIR
ls * | cpio -o > /dev/rmt/0h

. Shell编程中的注释以#开头
. 对shell变量进行数字运算,使用expr命令
expr integer operator integer
其中operator为+ - * / %, 但对*的使用要用转义符\,如:
$expr 4 \* 5
20
$int=`expr 5 + 7`
$echo $int
12

(3)Shell编程的参数传递, 可通过命令行参数以及交互式输入变量(read)

restoreall.sh 对backup.sh程序的备份磁带进行恢复
$cat > restoreall.sh
cd $WORKDIR
cpio -i < /dev/rmt/0h
^D
restore1.sh:只能恢复个文件
#restore1 --program to restore a single file
cd $WORKDIR
cpio -i $i < /dev/rmt/0h

$restore1 file1

恢复多个文件restoreany :
#restoreany --program to restore a single file
cd $WORKDIR
cpio -i $* < /dev/rmt/0h

$ restoreany file1 file2 file3


(4)条件判断
. if-then语句,格式如下:
if command_1
then
command_2
command_3
fi
command_4

在if-then语句中使用了命令返回码$?,即当command_1执行成功时才执行command_2和
command_3,而command_4总是执行.

示例程序unload: 在备份成功时删除原始文件,带有错误检查

cd
#备份时未考虑不成功的情况!
ls -a | cpio -o > /dev/rmt/0h
rm -rf *

改进如下:

#当使用了管道命令时,管理命令的返回代码为最后一个命令的返回代码
if ls -a | cpio -o > /dev/rmt/0h
then
rm -rf *
fi

. if-then-else语句
if command_1
then
command_2
else
command_3
fi

技巧: 由于shell对命令中的多余的空格不作任何处理,一个好的程序员会用这一特性
对自己的程序采用统一的缩进格式,以增强自己程序的可读性.

. 使用test命令进行进行条件测试
格式: test conditions

test在以下四种情况下使用: a. 字符比较 b.两个整数值的比较
c. 文件操作,如文件是否存在及文件的状态等
d. 逻辑操作,可以进行and/or,与其他条件联合使用

a. 测试字符数据: shell变量通常民政部下均作为字符变量
str1 = str2 二者相长,相同
str1 != str2 不同
-n string string不为空(长度不为零)
-z string string为空
string string不为空

例:
$ str1=abcd #在含有空格时必须用引号括起来
$ test $str1=abcd
$ echo $?
0
$ str1="abcd "
$ test $str1=abcd
$ echo $?
1
Note: 在test处理含有空格的变量时最好用引号将变量括起来,否则会出现错误的结果,
因为shell在处理命令行时将会去掉多余的空格,而用引号括起来则可以防止
shell去掉这些空格.
例:
$ str1=" "
$ test $str1
$ echo $?
1
$ test "$str1"
$ echo $?
0
$ test -n $str1
test: argument expected
$ test -n "$str1"
$ echo $?
0
$

b. 整数测试: test与expr相同,可以将字符型变量转换为整数进行操作,expr进行
整数的算术运算,而test则进行逻辑运算.

表达式 说明
---------------------------------------
int1 -eq int2 相等?
int1 -ne int2 不等?
int1 -gt int2 int1 > int2 ?
int1 -ge int2 int1 >= int2 ?
int1 -lt int2 int1 < int2 ?
int1 -le int2 int1 <= int2 ?

例:
$ int1=1234
$ int2=01234
$ test $int1 -eq $int2
$ echo $?
0

c. 文件测试:检查文件状态如存在及读写权限等

-r filename 用户对文件filename有读权限?
-w filename 用户对文件filename有写权限?
-x filename 用户对文件filename有可执行权限?
-f filename 文件filename为普通文件?
-d filename 文件filename为目录?
-c filename 文件filename为字符设备文件?
-b filename 文件filename为块设备文件?
-s filename 文件filename大小不为零?
-t fnumb 与文件描述符fnumb(默认值为1)相关的设备是一个终端设备?

d. 测试条件之否定,使用!
例:
$ cat /dev/null > empty
$ test -r empty
$ echo $?
0
$ test -s empty
1
$ test ! -s empty
$ echo $?
0
e. 测试条件之逻辑运算
-a And
-o Or

例: $ test -r empty -a -s empty
$ echo $?
1
f. 进行test测试的标准方法
因为test命令在 shell编程中占有很重要的地位,为了使shell能同其他编程语言一样
便于阅读和组织, Bourne Shell在使用test测试时使用了另一种方法:用方括号将整个
test测试括起来:

$ int1=4
$ [ $int1 -gt 2 ]
$ echo $?
0

例: 重写unload程序,使用test测试
#!/bin/sh
#unload - program to backup and remove files
#syntax: unload directory

#check arguments
if [ $# -ne 1 ]
then
echo "usage: directory"
exit 1
fi

#check for valid directory name
if [ ! -d "" ]
then
echo " is not a directory"
exit 2
fi

cd

ls -a | cpio -o > /dev/rmt/0h

if [ $? -eq 0 ]
then
rm -rf *
else
echo "A problem has occured in creating backup"
echo "The directory will not be ereased"
echo "Please check the backup device"
exit 3
fi
# end of unload

在如上示例中出现了exit, exit有两个作用:一是停止程序中其他命令的执行,二是
设置程序的退出状态

g. if嵌套及elif结构
if command
then
command
else
if command
then
command
else
if command
then
command
fi
fi
fi

改进:使用elif结构
if command
then
command
elif command
then
command
elif command
then
command
fi

elif结构同if结构类似,但结构更清淅,其执行结果完全相同.


-------------未完,待续----------



表示内容通常为当前执行程序的文件名。
.防止变量值被替换 readonly variable
.使用export命令输出变量,使得变量对子shell可用,当shell执行一下程序时,shell
将为其设置一个新的环境让其执行,这称之了subshell. 在Bourne Shell中变量通常
被认为是本地变量,也就是说在对其赋值之外的shell环境之外是不认识此变量的。使
用export对subshell可用。

例:对变量PS1的export操作,shell的提示符将发生变化。
$ PS1=`hostname`$
peony$sh
$ echo $PS1
$ <-输出结果
$ exit
peony$export PS1
peony$sh
peony$ echo $PS1
peony$ <-输出结果
peony$


3.Shell Script编程
目的:使用UNIX所提供的最常用工具来完成所需复杂任务的强大功能。

(1)最简单的Shell 编程
$ls -R / |grep myname |more

每天数据的备份:
$ cd /usr/yourname; ls * |cpio -o > /dev/rmt/0h

书写程序的目的是一次编程,多次使用(执行)!

$ cat > backup.sh
cd /home/hbwork
ls * | cpio -o > /dev/rmt/0h
^D

执行:
$ sh backup.sh

或:
$ chmod +x backup.sh
$ ./backup.sh

技巧:在shell script中加入必要的注释,以便以后阅读及维护。

(2)shell是一个(编程)语言
同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script
编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子
程序及以中断处理等。

. 在shell编程中使用数据变量可以将程序变量更为通用,如在上面backup.sh中:
cd $WORKDIR
ls * | cpio -o > /dev/rmt/0h

. Shell编程中的注释以#开头
. 对shell变量进行数字运算,使用expr命令
expr integer operator integer
其中operator为+ - * / %, 但对*的使用要用转义符\,如:
$expr 4 \* 5
20
$int=`expr 5 + 7`
$echo $int
12

(3)Shell编程的参数传递, 可通过命令行参数以及交互式输入变量(read)

restoreall.sh 对backup.sh程序的备份磁带进行恢复
$cat > restoreall.sh
cd $WORKDIR
cpio -i < /dev/rmt/0h
^D
restore1.sh:只能恢复一个文件
#restore1 --program to restore a single file
cd $WORKDIR
cpio -i $i < /dev/rmt/0h

$restore1 file1

恢复多个文件restoreany :
#restoreany --program to restore a single file
cd $WORKDIR
cpio -i $* < /dev/rmt/0h

$ restoreany file1 file2 file3


(4)条件判断
. if-then语句,格式如下:
if command_1
then
command_2
command_3
fi
command_4

在if-then语句中使用了命令返回码$?,即当command_1执行成功时才执行command_2和
command_3,而command_4总是执行.

示例程序unload: 在备份成功时删除原始文件,带有错误检查

cd
#备份时未考虑不成功的情况!
ls -a | cpio -o > /dev/rmt/0h
rm -rf *

改进如下:

#当使用了管道命令时,管理命令的返回代码为最后一命令的返回代码
if ls -a | cpio -o > /dev/rmt/0h
then
rm -rf *
fi

. if-then-else语句
if command_1
then
command_2
else
command_3
fi

技巧: 由于shell对命令中的多余的空格不作任何处理,一个好的程序员会用这一特性
对自己的程序采用统一的缩进格式,以增强自己程序的可读性.

. 使用test命令进行进行条件测试
格式: test conditions

test在以下四种情况下使用: a. 字符比较 b.两个整数值的比较
c. 文件操作,如文件是否存在及文件的状态等
d. 逻辑操作,可以进行and/or,与其他条件联合使用

a. 测试字符数据: shell变量通常民政部下均作为字符变量
str1 = str2 二者相长,相同
str1 != str2 不同
-n string string不为空(长度不为零)
-z string string为空
string string不为空

例:
$ str1=abcd #在含有空格时必须用引号括起来
$ test $str1=abcd
$ echo $?
0
$ str1="abcd "
$ test $str1=abcd
$ echo $?
1
Note: 在test处理含有空格的变量时最好用引号将变量括起来,否则会出现错误的结果,
因为shell在处理命令行时将会去掉多余的空格,而用引号括起来则可以防止
shell去掉这些空格.
例:
$ str1=" "
$ test $str1
$ echo $?
1
$ test "$str1"
$ echo $?
0
$ test -n $str1
test: argument expected
$ test -n "$str1"
$ echo $?
0
$

b. 整数测试: test与expr相同,可以将字符型变量转换为整数进行操作,expr进行
整数的算术运算,而test则进行逻辑运算.

表达式 说明
---------------------------------------
int1 -eq int2 相等?
int1 -ne int2 不等?
int1 -gt int2 int1 > int2 ?
int1 -ge int2 int1 >= int2 ?
int1 -lt int2 int1 < int2 ?
int1 -le int2 int1 <= int2 ?

例:
$ int1=1234
$ int2=01234
$ test $int1 -eq $int2
$ echo $?
0

c. 文件测试:检查文件状态如存在及读写权限等

-r filename 用户对文件filename有读权限?
-w filename 用户对文件filename有写权限?
-x filename 用户对文件filename有可执行权限?
-f filename 文件filename为普通文件?
-d filename 文件filename为目录?
-c filename 文件filename为字符设备文件?
-b filename 文件filename为块设备文件?
-s filename 文件filename大小不为零?
-t fnumb 与文件描述符fnumb(默认值为1)相关的设备是一个终端设备?

d. 测试条件之否定,使用!
例:
$ cat /dev/null > empty
$ test -r empty
$ echo $?
0
$ test -s empty
1
$ test ! -s empty
$ echo $?
0
e. 测试条件之逻辑运算
-a And
-o Or

例: $ test -r empty -a -s empty
$ echo $?
1
f. 进行test测试的标准方法
因为test命令在 shell编程中占有很重要的地位,为了使shell能同其他编程语言一样
便于阅读和组织, Bourne Shell在使用test测试时使用了另一种方法:用方括号将整个
test测试括起来:

$ int1=4
$ [ $int1 -gt 2 ]
$ echo $?
0

例: 重写unload程序,使用test测试
#!/bin/sh
#unload - program to backup and remove files
#syntax: unload directory

#check arguments
if [ $# -ne 1 ]
then
echo "usage:
 
Bourne Shell

介绍:Bourne Shell 基础及其他很多有用的特性,shell编程及组织。

主要内容:
.shell基础 基本介绍,环境,选项,特殊字符
.shell变量 用户定义变量,环境变量,位置变量(shell 参数)
.shell script编程
条件测试,循环及重复控制
.shell定制

1.shell基础知识
作者:Stephen Bourne 在Bell实验室开发
建议:man sh 查看相关UNIX上的改进或特性

(1)shell提示符及其环境
/etc/passwd文件
提示符:$
/etc/profile $HOME/.profile
(2)shell执行选项
-n 测试shell script语法结构,只读取shell script但不执行
-x 进入跟踪方式,显示所执行的每一条命令,用于调度
-a Tag all variables for export
-c "string" 从strings中读取命令
-e 非交互方式
-f 关闭shell文件名产生功能
-h locate and remember functions as defind
-i 交互方式
-k 从环境变量中读取命令的参数
-r 限制方式
-s 从标准输入读取命令
-t 执行命令后退出(shell exits)
-u 在替换中如使用未定义变量为错误
-v verbose,显示shell输入行

这些选项可以联合使用,但有些显然相互冲突,如-e和-i.

(3)受限制shell(Restircted Shell)
sh -r 或 /bin/rsh

不能执行如下操作:cd, 更改PATH,指定全路径名,输出重定向,因此可以提供一个较
好的控制和安全机制。通常rsh用于应用型用户及拨号用户,这些用户通常是看不到提
示符的。通常受限制用户的主目录是不可写的。

不足:如果用户可以调用sh,则rsh的限制将不在起作用,事实上如果用户在vi及more
程序中调用shell,而这时rsh的限制将不再起作用。

(4)用set改变 shell选项
用户可以在$提示符下用set命令来设置或取消shell的选项。使用-设置选项,+取消相应
选项,大多数UNIX系统允许a,e,f,h,k,n,u,v和x的开关设置/取消。

set -xv
启动跟踪方式;显示所有的命令及替换,同样显示输入。
set -tu
关闭在替换时对未定义变量的检查。

使用echo $-显示所有已设置的shell选项。


(5)用户启动文件 .profile
PATH=$PATH:/usr/loacl/bin; export PATH

(6)shell环境变量
CDPATH 用于cd命令的查找路径
HOME /etc/passwd文件中列出的用户主目录
IFS Internal Field Separator,默认为空格,tab及换行符
MAIL /var/mail/$USERNAME mail等程序使用
PATH
PS1,PS2 默认提示符($)及换行提示符(>)
TERM 终端类型,常用的有vt100,ansi,vt200,xterm等

示例:$PS1="test:";export PS1
test: PS1="$";export PS1
$echo $MAIL
/var/mail/username
(7)保留字符及其含义
$ shell变量名的开始,如$var
| 管道,将标准输出转到下一个命令的标准输入
# 注释开始
& 在后台执行一个进程
? 匹配一个字符
* 匹配0到多个字符(与DOS不同,可在文件名中间使用,并且含.)
$- 使用set及执行时传递给shell的标志位
$! 最后一个子进程的进程号
$# 传递给shell script的参数个数
$* 传递给shell script的参数
$@ 所有参数,个别的用双引号括起来
$? 上一个命令的返回代码
当前shell的名字
$n (n:1-) 位置参数
$$ 进程标识号(Process Identifier Number, PID)
>file 输出重定向
`command` 命令替换,如 filename=`basename /usr/local/bin/tcsh`
>>fiile 输出重定向,append

转义符及单引号:
$echo "$HOME $PATH"
/home/hbwork /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:
$echo '$HOME $PATH'
$HOME $PATH
$echo $HOME $PATH
$HOME /opt/kde/bin:/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/hbwork/bin

其他:
$dir=ls
$$dir
$alias dir ls
$dir

ls > filelist
ls >> filelist
wc -l < filelist
wc -l filelist
sleep 5; echo 5 seconds reaches; ls -l
ps ax |egrep inetd
find / -name core -exec rm {} \; &
filename=`date "+%Y%m%d"`.log

2. shell变量
变量:代表某些值的符号,如$HOME,cd命令查找$HOME,在计算机语言中可以使用变量可以
进行多种运算和控制。

Bourne Shell有如下四种变量:
.用户自定义变量
.位置变量即 shell script之参数
.预定义变量(特殊变量)
.环境变量(参考shell定制部分)
(1)用户自定义变量(数据的存储)
$ COUNT=1
$ NAME="He Binwu"

技巧:因为大部分UNIX命令使用小写字符,因此在shell编程中通常使用全大写变量,
当然这并不是强制性的,但使用大写字符可以在编程中方便地识别变量。

变量的调用:在变量前加$
$ echo $HOME
/home/hbwork
$ WEEK=Satur
$ echo Today is $WEEKday
Today is
$echo Today is ${WEEK}day
Today is Saturday

Shell变量赋值从右从左进行(Linux Shell/bash从左向右赋值!)
$ X=$Y Y=y
$ echo $X
y
$ Z=z Y=$Z
$ echo $Y

$

使用unset命令删除变量的赋值
$ Z=hello
$ echo $Z
hello
$ unset Z
$ echo $Z

$

有条件的命令替换
在Bourne Shell中可以使变量替换在特定条件下执行,即有条的环境变量替换。
这种变量替换总是用大括号括起来的。

.设置变量的默认值
在变量未赋值之前其值为空。Bourne Shell允许对变量设置默认值,其格式如下:
${variable:-defaultvalue}
例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:-there}
Hello there
$ echo $UNAME #变量值并未发生变化

$ UNAME=hbwork
$ echo Hello ${UNAME:-there}
Hello hbwork
$
.另一种情况:改变变量的值,格式如下:
${variable:=value}

例:
$ echo Hello $UNAME
Hello
$ echo Hello ${UNAME:=there}
Hello there
$ echo $UNAME #变量值并未发生变化
there
$
.变量替换中使用命令替换
$USERDIR=${$MYDIR:-`pwd`}

.在变量已赋值时进行替换 ${variable:+value}
.带有错误检查的有条件变量替换
${variable:?value}
例:
$ UNAME=
$ echo ${UNAME:?"UNAME has not been set"}
UNAME: UNAME has not been set
$ echo ${UNAME:?}
UNAME: parameter null or not set

(2)位置变量(Shell参数)
在shell script中位置参数可用..表示,表示内容通常为当前执行程序的文件名。
.防止变量值被替换 readonly variable
.使用export命令输出变量,使得变量对子shell可用,当shell执行一下程序时,shell
将为其设置一个新的环境让其执行,这称之了subshell. 在Bourne Shell中变量通常
被认为是本地变量,也就是说在对其赋值之外的shell环境之外是不认识此变量的。使
用export对subshell可用。

例:对变量PS1的export操作,shell的提示符将发生变化。
$ PS1=`hostname`$
peony$sh
$ echo $PS1
$ <-输出结果
$ exit
peony$export PS1
peony$sh
peony$ echo $PS1
peony$ <-输出结果
peony$


3.Shell Script编程
目的:使用UNIX所提供的最常用工具来完成所需复杂任务的强大功能。

(1)最简单的Shell 编程
$ls -R / |grep myname |more

每天数据的备份:
$ cd /usr/yourname; ls * |cpio -o > /dev/rmt/0h

书写程序的目的是一次编程,多次使用(执行)!

$ cat > backup.sh
cd /home/hbwork
ls * | cpio -o > /dev/rmt/0h
^D

执行:
$ sh backup.sh

或:
$ chmod +x backup.sh
$ ./backup.sh

技巧:在shell script中加入必要的注释,以便以后阅读及维护。

(2)shell是一个(编程)语言
同传统的编程语言一样,shell提供了很多特性,这些特性可以使你的shell script
编程更为有用,如:数据变量、参数传递、判断、流程控制、数据输入和输出,子
程序及以中断处理等。

. 在shell编程中使用数据变量可以将程序变量更为通用,如在上面backup.sh中:
cd $WORKDIR
ls * | cpio -o > /dev/rmt/0h

. Shell编程中的注释以#开头
. 对shell变量进行数字运算,使用expr命令
expr integer operator integer
其中operator为+ - * / %, 但对*的使用要用转义符\,如:
$expr 4 \* 5
20
$int=`expr 5 + 7`
$echo $int
12

(3)Shell编程的参数传递, 可通过命令行参数以及交互式输入变量(read)

restoreall.sh 对backup.sh程序的备份磁带进行恢复
$cat > restoreall.sh
cd $WORKDIR
cpio -i < /dev/rmt/0h
^D
restore1.sh:只能恢复一个文件
#restore1 --program to restore a single file
cd $WORKDIR
cpio -i $i < /dev/rmt/0h

$restore1 file1

恢复多个文件restoreany :
#restoreany --program to restore a single file
cd $WORKDIR
cpio -i $* < /dev/rmt/0h

$ restoreany file1 file2 file3


(4)条件判断
. if-then语句,格式如下:
if command_1
then
command_2
command_3
fi
command_4

在if-then语句中使用了命令返回码$?,即当command_1执行成功时才执行command_2和
command_3,而command_4总是执行.

示例程序unload: 在备份成功时删除原始文件,带有错误检查

cd
#备份时未考虑不成功的情况!
ls -a | cpio -o > /dev/rmt/0h
rm -rf *

改进如下:

#当使用了管道命令时,管理命令的返回代码为最后一个命令的返回代码
if ls -a | cpio -o > /dev/rmt/0h
then
rm -rf *
fi

. if-then-else语句
if command_1
then
command_2
else
command_3
fi

技巧: 由于shell对命令中的多余的空格不作任何处理,一个好的程序员会用这一特性
对自己的程序采用统一的缩进格式,以增强自己程序的可读性.

. 使用test命令进行进行条件测试
格式: test conditions

test在以下四种情况下使用: a. 字符比较 b.两个整数值的比较
c. 文件操作,如文件是否存在及文件的状态等
d. 逻辑操作,可以进行and/or,与其他条件联合使用

a. 测试字符数据: shell变量通常民政部下均作为字符变量
str1 = str2 二者相长,相同
str1 != str2 不同
-n string string不为空(长度不为零)
-z string string为空
string string不为空

例:
$ str1=abcd #在含有空格时必须用引号括起来
$ test $str1=abcd
$ echo $?
0
$ str1="abcd "
$ test $str1=abcd
$ echo $?
1
Note: 在test处理含有空格的变量时最好用引号将变量括起来,否则会出现错误的结果,
因为shell在处理命令行时将会去掉多余的空格,而用引号括起来则可以防止
shell去掉这些空格.
例:
$ str1=" "
$ test $str1
$ echo $?
1
$ test "$str1"
$ echo $?
0
$ test -n $str1
test: argument expected
$ test -n "$str1"
$ echo $?
0
$

b. 整数测试: test与expr相同,可以将字符型变量转换为整数进行操作,expr进行
整数的算术运算,而test则进行逻辑运算.

表达式 说明
---------------------------------------
int1 -eq int2 相等?
int1 -ne int2 不等?
int1 -gt int2 int1 > int2 ?
int1 -ge int2 int1 >= int2 ?
int1 -lt int2 int1 < int2 ?
int1 -le int2 int1 <= int2 ?

例:
$ int1=1234
$ int2=01234
$ test $int1 -eq $int2
$ echo $?
0

c. 文件测试:检查文件状态如存在及读写权限等

-r filename 用户对文件filename有读权限?
-w filename 用户对文件filename有写权限?
-x filename 用户对文件filename有可执行权限?
-f filename 文件filename为普通文件?
-d filename 文件filename为目录?
-c filename 文件filename为字符设备文件?
-b filename 文件filename为块设备文件?
-s filename 文件filename大小不为零?
-t fnumb 与文件描述符fnumb(默认值为1)相关的设备是一个终端设备?

d. 测试条件之否定,使用!
例:
$ cat /dev/null > empty
$ test -r empty
$ echo $?
0
$ test -s empty
1
$ test ! -s empty
$ echo $?
0
e. 测试条件之逻辑运算
-a And
-o Or

例: $ test -r empty -a -s empty
$ echo $?
1
f. 进行test测试的标准方法
因为test命令在 shell编程中占有很重要的地位,为了使shell能同其他编程语言一样
便于阅读和组织, Bourne Shell在使用test测试时使用了另一种方法:用方括号将整个
test测试括起来:

$ int1=4
$ [ $int1 -gt 2 ]
$ echo $?
0

例: 重写unload程序,使用test测试
#!/bin/sh
#unload - program to backup and remove files
#syntax: unload directory

#check arguments
if [ $# -ne 1 ]
then
echo "usage: directory"
exit 1
fi

#check for valid directory name
if [ ! -d "" ]
then
echo " is not a directory"
exit 2
fi

cd

ls -a | cpio -o > /dev/rmt/0h

if [ $? -eq 0 ]
then
rm -rf *
else
echo "A problem has occured in creating backup"
echo "The directory will not be ereased"
echo "Please check the backup device"
exit 3
fi
# end of unload

在如上示例中出现了exit, exit有两个作用:一是停止程序中其他命令的执行,二是
设置程序的退出状态

g. if嵌套及elif结构
if command
then
command
else
if command
then
command
else
if command
then
command
fi
fi
fi

改进:使用elif结构
if command then
command
elif command
then
command
elif command
then
command
fi

elif结构同if结构类似,但结构更清淅,其执行结果完全相同.


-------------未完,待续----------



directory"
exit 1
fi

#check for valid directory name
if [ ! -d "" ]
then
echo " is not a directory"
exit 2
fi

cd

ls -a | cpio -o > /dev/rmt/0h

if [ $? -eq 0 ]
then
rm -rf *
else
echo "A problem has occured in creating backup"
echo "The directory will not be ereased"
echo "Please check the backup device"
exit 3
fi
# end of unload

在如上示例中出现了exit, exit有两个作用:一是停止程序中其他命令的执行,二是
设置程序的退出状态

g. if嵌套及elif结构
if command
then
command
else
if command
then
command
else
if command
then
command
fi
fi
fi

改进:使用elif结构
if command
then
command
elif command
then
command
elif command
then
command
fi

elif结构同if结构类似,但结构更清淅,其执行结果完全相同.


-------------未完,待续----------












- 作者: colding 2006年07月5日, 星期三 11:17  回复(0) |  引用(0) 加入博采

How to write a shell script

How to write a shell script

Introduction

A shell is a command line interpretor. It takes commands and executes them. As such, it implements a programming language. The Bourne shell is used to create shell scripts -- ie. programs that are interpreted/executed by the shell. You can write shell scripts with the C-shell; however, this is not covered here.

Creating a Script

Suppose you often type the command
    find . -name file -print
and you'd rather type a simple command, say
    sfind file
Create a shell script
    % cd ~/bin
% emacs sfind
% page sfind
find . -name $1 -print
% chmod a+x sfind
% rehash
% cd /usr/local/bin
% sfind tcsh
./shells/tcsh

Observations

This quick example is far from adequate but some observations:
  1. Shell scripts are simple text files created with an editor.
  2. Shell scripts are marked as executeable
        %chmod a+x sfind
    
  3. Should be located in your search path and ~/bin should be in your search path.
  4. You likely need to rehash if you're a Csh (tcsh) user (but not again when you login).
  5. Arguments are passed from the command line and referenced. For example, as $1.

#!/bin/sh

All Bourne Shell scripts should begin with the sequence
    #!/bin/sh
From the man page for exec(2):

"On the first line of an interpreter script, following the "#!", is the name of a program which should be used to interpret the contents of the file. For instance, if the first line contains "#! /bin/sh", then the con- tents of the file are executed as a shell script."

You can get away without this, but you shouldn't. All good scripts state the interpretor explicitly. Long ago there was just one (the Bourne Shell) but these days there are many interpretors -- Csh, Ksh, Bash, and others.

Comments

Comments are any text beginning with the pound (#) sign. A comment can start anywhere on a line and continue until the end of the line.

Search Path

All shell scripts should include a search path specifica- tion:
    PATH=/usr/ucb:/usr/bin:/bin; export PATH
A PATH specification is recommended -- often times a script will fail for some people because they have a different or incomplete search path.

The Bourne Shell does not export environment variables to children unless explicitly instructed to do so by using the export command.

Argument Checking

A good shell script should verify that the arguments sup- plied (if any) are correct.
    if [ $# -ne 3 ]; then
echo 1>&2 Usage: $0 19 Oct 91
exit 127
fi
This script requires three arguments and gripes accordingly.

Exit status

All Unix utilities should return an exit status.
    # is the year out of range for me?

if [ $year -lt 1901 -o $year -gt 2099 ]; then
echo 1>&2 Year \"$year\" out of range
exit 127
fi

etc...

# All done, exit ok

exit 0
A non-zero exit status indicates an error condition of some sort while a zero exit status indicates things worked as expected.

On BSD systems there's been an attempt to categorize some of the more common exit status codes. See /usr/include/sysexits.h.

Using exit status

Exit codes are important for those who use your code. Many constructs test on the exit status of a command.

The conditional construct is:

    if command; then
command
fi
For example,
    if tty -s; then
echo Enter text end with \^D
fi
Your code should be written with the expectation that others will use it. Making sure you return a meaningful exit status will help.

Stdin, Stdout, Stderr

Standard input, output, and error are file descriptors 0, 1, and 2. Each has a particular role and should be used accordingly:
    # is the year out of range for me?

if [ $year -lt 1901 -o $year -gt 2099 ]; then
echo 1>&2 Year \"$year\" out of my range
exit127
fi

etc...

# ok, you have the number of days since Jan 1, ...

case `expr $days % 7` in
0)
echo Mon;;
1)
echo Tue;;

etc...
Error messages should appear on stderr not on stdout! Output should appear on stdout. As for input/output dialogue:
    # give the fellow a chance to quit

if tty -s ; then
echo This will remove all files in $* since ...
echo $n Ok to procede? $c; read ans
case "$ans" in
n*|N*)
echo File purge abandoned;
exit 0 ;;
esac
RM="rm -rfi"
else
RM="rm -rf"
fi
Note: this code behaves differently if there's a user to communicate with (ie. if the standard input is a tty rather than a pipe, or file, or etc. See tty(1)).

Language Constructs

    For loop iteration

    Substitute values for variable and perform task:

        for variable in word ...
    do
    command
    done
    For example:
        for i in `cat $LOGS`
    do
    mv $i $i.$TODAY
    cp /dev/null $i
    chmod 664 $i
    done
    Alternatively you may see:
        for variable in word ...; do command; done
  • Case

    Switch to statements depending on pattern match

        case word in
    [ pattern [ | pattern ... ] )
    command ;; ] ...
    esac
    For example:
        case "$year" in

    [0-9][0-9])
    year=19${year}
    years=`expr $year - 1901`
    ;;
    [0-9][0-9][0-9][0-9])
    years=`expr $year - 1901`
    ;;
    *)
    echo 1>&2 Year \"$year\" out of range ...
    exit 127
    ;;
    esac
  • Conditional Execution

    Test exit status of command and branch

        if command
    then
    command
    [ else
    command ]
    fi
    For example:
        if [ $# -ne 3 ]; then
    echo 1>&2 Usage: $0 19 Oct 91
    exit 127
    fi
    Alternatively you may see:
        if command; then command; [ else command; ] fi
  • While/Until Iteration

    Repeat task while command returns good exit status.

        {while | until} command
    do
    command
    done
    For example:
        # for each argument mentioned, purge that directory

    while [ $# -ge 1 ]; do
    _purge $1
    shift
    done
    Alternatively you may see:
        while command; do command; done
  • Variables

    Variables are sequences of letters, digits, or underscores beginning with a letter or underscore. To get the contents of a variable you must prepend the name with a $.

    Numeric variables (eg. like $1, etc.) are positional vari- ables for argument communication.

    • Variable Assignment

      Assign a value to a variable by variable=value. For example:

          PATH=/usr/ucb:/usr/bin:/bin; export PATH
      or
          TODAY=`(set \`date\`; echo $1)`
    • Exporting Variables

      Variables are not exported to children unless explicitly marked.

          # We MUST have a DISPLAY environment variable

      if [ "$DISPLAY" = "" ]; then
      if tty -s ; then
      echo "DISPLAY (`hostname`:0.0)? \c";
      read DISPLAY
      fi
      if [ "$DISPLAY" = "" ]; then
      DISPLAY=`hostname`:0.0
      fi
      export DISPLAY
      fi
      Likewise, for variables like the PRINTER which you want hon- ored by lpr(1). From a user's .profile:
          PRINTER=PostScript; export PRINTER
      Note: that the Cshell exports all environment variables.

    • Referencing Variables

      Use $variable (or, if necessary, ${variable}) to reference the value.

          # Most user's have a /bin of their own

      if [ "$USER" != "root" ]; then
      PATH=$HOME/bin:$PATH
      else
      PATH=/etc:/usr/etc:$PATH
      fi
      The braces are required for concatenation constructs.
      $p_01
      The value of the variable "p_01".
      ${p}_01
      The value of the variable "p" with "_01" pasted onto the end.

    • Conditional Reference

      ${variable-word}
      If the variable has been set, use it's value, else use word.
      POSTSCRIPT=${POSTSCRIPT-PostScript};
      export POSTSCRIPT

      ${variable:-word}
      If the variable has been set and is not null, use it's value, else use word.

      These are useful constructions for honoring the user envi- ronment. Ie. the user of the script can override variable assignments. Cf. programs like lpr(1) honor the PRINTER environment variable, you can do the same trick with your shell scripts.

      ${variable:?word}
      If variable is set use it's value, else print out word and exit. Useful for bailing out.

    • Arguments

      Command line arguments to shell scripts are positional vari- ables:

      $0, $1, ...
      The command and arguments. With $0 the command and the rest the arguments.
      $#
      The number of arguments.
      $*, $@
      All the arguments as a blank separated string. Watch out for "$*" vs. "$@".
      And, some commands:
      shift
      Shift the postional variables down one and decrement number of arguments.
      set arg arg ...
      Set the positional variables to the argument list.

      Command line parsing uses shift:

          # parse argument list

      while [ $# -ge 1 ]; do
      case $1 in
      process arguments...
      esac
      shift
      done
      A use of the set command:
          # figure out what day it is

      TODAY=`(set \`date\`; echo $1)`

      cd $SPOOL

      for i in `cat $LOGS`
      do
      mv $i $i.$TODAY
      cp /dev/null $i
      chmod 664 $i
      done
    • Special Variables
      $$
      Current process id. This is very useful for constructing temporary files.
               tmp=/tmp/cal0$$
      trap "rm -f $tmp /tmp/cal1$$ /tmp/cal2$$"
      trap exit 1 2 13 15
      /usr/lib/calprog >$tmp

      $?
      The exit status of the last command.
               $command
      # Run target file if no errors and ...

      if [ $? -eq 0 ]
      then
      etc...
      fi

  • Quotes/Special Characters

    Special characters to terminate words:

          ; & ( ) | ^ < > new-line space tab
    These are for command sequences, background jobs, etc. To quote any of these use a backslash (\) or bracket with quote marks ("" or '').

    Single Quotes

    Within single quotes all characters are quoted -- including the backslash. The result is one word.

             grep :${gid}: /etc/group | awk -F: '{print $1}'
    Double Quotes

    Within double quotes you have variable subsitution (ie. the dollar sign is interpreted) but no file name generation (ie. * and ? are quoted). The result is one word.

             if [ ! "${parent}" ]; then
    prent=${people}/${group}/${user}
    fi
    Back Quotes

    Back quotes mean run the command and substitute the output.

             if [ "`echo -n`" = "-n" ]; then
    n=""
    c="\c"
    else
    n="-n"
    c=""
    fi
    and
             TODAY=`(set \`date\`; echo $1)`
  • Functions

    Functions are a powerful feature that aren't used often enough. Syntax is

        name ()
    {
    commands
    }
    For example:
        # Purge a directory

    _purge()
    {
    # there had better be a directory

    if [ ! -d $1 ]; then
    echo $1: No such directory 1>&2
    return
    fi

    etc...
    }
    Within a function the positional parmeters $0, $1, etc. are the arguments to the function (not the arguments to the script).

    Within a function use return instead of exit.

    Functions are good for encapsulations. You can pipe, redi- rect input, etc. to functions. For example:

        # deal with a file, add people one at a time

    do_file()
    {
    while parse_one

    etc...
    }

    etc...

    # take standard input (or a specified file) and do it.

    if [ "$1" != "" ]; then
    cat $1 | do_file
    else
    do_file
    fi
  • Sourcing commands

    You can execute shell scripts from within shell scripts. A couple of choices:

    sh command

    This runs the shell script as a separate shell. For example, on Sun machines in /etc/rc:

             sh /etc/rc.local
    . command

    This runs the shell script from within the current shell script. For example:

             # Read in configuration information
    . /etc/hostconfig
    What are the virtues of each? What's the difference? The second form is useful for configuration files where environment variable are set for the script. For example:
        for HOST in $HOSTS; do

    # is there a config file for this host?

    if [ -r ${BACKUPHOME}/${HOST} ]; then
    . ${BACKUPHOME}/${HOST}
    fi
    etc...
    Using configuration files in this manner makes it possible to write scripts that are automatically tailored for differ- ent situations.

Some Tricks

  • Test

    The most powerful command is test(1).

        if test expression; then

    etc...
    and (note the matching bracket argument)
        if [ expression ]; then

    etc...
    On System V machines this is a builtin (check out the com- mand /bin/test).

    On BSD systems (like the Suns) compare the command /usr/bin/test with /usr/bin/[.

    Useful expressions are:

    test { -w, -r, -x, -s, ... } filename
    is file writeable, readable, executeable, empty, etc?
    test n1 { -eq, -ne, -gt, ... } n2
    are numbers equal, not equal, greater than, etc.?
    test s1 { =, != } s2
    Are strings the same or different?
    test cond1 { -o, -a } cond2
    Binary or; binary and; use ! for unary negation.

    For example

        if [ $year -lt 1901  -o  $year -gt 2099 ]; then
    echo 1>&2 Year \"$year\" out of range
    exit 127
    fi
    Learn this command inside out! It does a lot for you.

  • String matching

    The test command provides limited string matching tests. A more powerful trick is to match strings with the case switch.

        # parse argument list

    while [ $# -ge 1 ]; do
    case $1 in
    -c*) rate=`echo $1 | cut -c3-`;;
    -c) shift; rate=$1 ;;
    -p*) prefix=`echo $1 | cut -c3-`;;
    -p) shift; prefix=$1 ;;
    -*) echo $Usage; exit 1 ;;
    *) disks=$*; break ;;
    esac

    shift

    done
    Of course getopt would work much better.

  • SysV vs BSD echo

    On BSD systems to get a prompt you'd say:

        echo -n Ok to procede?;  read ans
    On SysV systems you'd say:
        echo Ok to procede? \c; read ans
    In an effort to produce portable code we've been using:
        # figure out what kind of echo to use

    if [ "`echo -n`" = "-n" ]; then
    n=""; c="\c"
    else
    n="-n"; c=""
    fi

    etc...

    echo $n Ok to procede? $c; read ans
  • Is there a person?

    The Unix tradition is that programs should execute as qui- etly as possible. Especially for pipelines, cron jobs, etc.

    User prompts aren't required if there's no user.

        # If there's a person out there, prod him a bit.

    if tty -s; then
    echo Enter text end with \^D
    fi
    The tradition also extends to output.
        # If the output is to a terminal, be verbose

    if tty -s <&1; then
    verbose=true
    else
    verbose=false
    fi
    Beware: just because stdin is a tty that doesn't mean that stdout is too. User prompts should be directed to the user terminal.
        # If there's a person out there, prod him a bit.

    if tty -s; then
    echo Enter text end with \^D >&0
    fi
    Have you ever had a program stop waiting for keyboard input when the output is directed elsewhere?

  • Creating Input

    We're familiar with redirecting input. For example:

        # take standard input (or a specified file) and do it.

    if [ "$1" != "" ]; then
    cat $1 | do_file
    else
    do_file
    fi
    alternatively, redirection from a file:
        # take standard input (or a specified file) and do it.

    if [ "$1" != "" ]; then
    do_file < $1
    else
    do_file
    fi
    You can also construct files on the fly.
        rmail bsmtp <<$1@newshost.uwo.ca>
    rcpt to:
    data
    from: <$1@newshost.uwo.ca>
    to:
    Subject: Signon $2

    subscribe $2 Usenet Feeder at UWO
    .
    quit
    EOF
    Note: that variables are expanded in the input.

  • String Manipulations

    One of the more common things you'll need to do is parse strings. Some tricks

        TIME=`date | cut -c12-19`

    TIME=`date | sed 's/.* .* .* \(.*\) .* .*/\1/'`

    TIME=`date | awk '{print $4}'`

    TIME=`set \`date\`; echo $4`

    TIME=`date | (read u v w x y z; echo $x)`
    With some care, redefining the input field separators can help.
        #!/bin/sh
    # convert IP number to in-addr.arpa name

    name()
    { set `IFS=".";echo $1`
    echo $4.$3.$2.$1.in-addr.arpa
    }

    if [ $# -ne 1 ]; then
    echo 1>&2 Usage: bynum IP-address
    exit 127
    fi

    add=`name $1`

    nslookup < < EOF | grep "$add" | sed 's/.*= //'
    set type=any
    $add
    EOF
  • Debugging

    The shell has a number of flags that make debugging easier:

    sh -n command

    Read the shell script but don't execute the commands. IE. check syntax.

    sh -x command

    Display commands and arguments as they're executed. In a lot of my shell scripts you'll see

        # Uncomment the next line for testing
    # set -x

Based on An Introduction to Shell Programing by:

Reg Quinton
Computing and Communications Services
The University of Western Ontario
London, Ontario N6A 5B7
Canada

- 作者: colding 2006年07月5日, 星期三 11:01  回复(0) |  引用(0) 加入博采

用rsync对网站进行镜像备份[转]

用rsync对网站进行镜像备份


创建时间:2001-07-05
文章属性:原创
文章来源:http://xfocus.org/
文章提交:inburst (inburst_at_263.net)

用rsync对网站进行镜像备份

by inburst<inburst@263.net>
http://xfocus.org

对系统管理员来说,平时的工作重心应该集中在维护系统正常运转,能够正常提供服务上,这里往往牵涉到一个数据备份的问题,在我所了解

的情况中,有80%的系统管理员不是太关心自己服务器的安全性,但往往对备分镜像的技术相当感兴趣,但由于商业产品的软硬件价格都相当高

昂,因此往往会选择自由软件。这里准备介绍的rsync就是这样的软件,它可以满足绝大多数要求不是特别高的备份需求。

一、特性简介

rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync。它的特性如下:

1、可以镜像保存整个目录树和文件系统。
2、可以很容易做到保持原来文件的权限、时间、软硬链接等等。
3、无须特殊权限即可安装。
4、优化的流程,文件传输效率高。
5、可以使用rcp、ssh等方式来传输文件,当然也可以通过直接的socket连接。
6、支持匿名传输。

二、使用方法

rsync的使用方法很简单,我就举自己使用的例子来说明吧。

1、系统环境

rsync支持大多数的类unix系统,无论是Linux、Solaris还是BSD上都经过了良好的测试。我的系统环境为:

server: FreeBSD 4.3  ip: 192.168.168.52
client: Solaris 8    ip: 192.168.168.137
rsync 版本 2.4.6(可以从http://rsync.samba.org/rsync/获得最新版本)

2、配置server端的/etc/rsyncd.conf文件

bash-2.03# cat /etc/rsyncd.conf

uid = nobody
gid = nobody
use chroot = no         # 不使用chroot
max connections = 4         # 最大连接数为4
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log    # 日志记录文件

[inburst]            # 这里是认证的模块名,在client端需要指定
path = /home/inburst/python/    # 需要做镜像的目录
comment = BACKUP CLIENT IS SOLARIS 8 E250
ignore errors            # 可以忽略一些无关的IO错误
read only = yes            # 只读
list = no            # 不允许列文件
auth users = inburst        # 认证的用户名,如果没有这行,则表明是匿名
secrets file = /etc/inburst.pas    # 认证文件名

[web]
path = /usr/local/apache/htdocs/
comment = inburst.org web server

3、在server端生成一个密码文件/etc/inburst.pas

bash-2.03# cat /etc/inburst.pas
inburst:hack

出于安全目的,文件的属性必需是只有属主可读。

4、在server端将rsync以守护进程形式启动

bash-2.03# rsync --daemon

如果要在启动时把服务起来,有几种不同的方法,比如:

  a、加入inetd.conf

    编辑/etc/services,加入rsync   873/tcp,指定rsync的服务端口是873
    编加/etc/inetd.conf,加入rsync  stream  tcp   nowait  root  /bin/rsync rsync --daemon

  b、加入rc.local

    在各种操作系统中,rc文件存放位置不尽相同,可以修改使系统启动时rsync --daemon加载进去。

5、从client端进行测试

下面这个命令行中-vzrtopg里的v是verbose,z是压缩,r是recursive,topg都是保持文件原有属性如属主、时间的参数。--progress是指显示

出详细的进度情况,--delete是指如果服务器端删除了这一文件,那么客户端也相应把文件删除,保持真正的一致。后面的inburst@ip中,

inburst是指定密码文件中的用户名,之后的::inburst这一inburst是模块名,也就是在/etc/rsyncd.conf中自定义的名称。最后的/tmp是备份

到本地的目录名。

在这里面,还可以用-e ssh的参数建立起加密的连接。可以用--password-file=/password/path/file来指定密码文件,这样就可以在脚本中使

用而无需交互式地输入验证密码了,这里需要注意的是这份密码文件权限属性要设得只有属主可读。

bash-2.03# rsync -vzrtopg --progress --delete inburst@192.168.168.52::inburst /tmp/
Password:
receiving file list ... done
./
1
785 (100%)
1.py
4086 (100%)
2.py
10680 (100%)
a
0 (100%)
ip
3956 (100%)
./
wrote 190 bytes  read 5499 bytes  758.53 bytes/sec
total size is 19507  speedup is 3.43

6、创建更新脚本

如果有比较复杂的工作,利用一些常见的脚本语言可以有帮助。比如:

bash-2.03# cat /usr/local/bin/rsync.sh

#!/bin/sh
DATE=`date +%w`

rsync -vzrtopg --progress --delete inburst@192.168.168.52::inburst /home/quack/backup/$DATE --password-file=/etc/rsync.pass >

/var/log/rsync.$DATE

7、修改/etc/crontab做好定时

比如:

bash-2.03# echo "15      4       *       *       6       root    rsync.sh">>/etc/crontab

、FAQ

Q:如何通过ssh进行rsync,而且无须输入密码?
A:可以通过以下几个步骤

1. 通过ssh-keygen在server A上建立SSH keys,不要指定密码,你会在~/.ssh下看到identity和identity.pub文件
2. 在server B上的home目录建立子目录.ssh
3. 将A的identity.pub拷贝到server B上
4. 将identity.pub加到~[user b]/.ssh/authorized_keys
5. 于是server A上的A用户,可通过下面命令以用户B ssh到server B上了
                e.g. ssh -l userB serverB
这样就使server A上的用户A就可以ssh以用户B的身份无需密码登陆到server B上了。

Q:如何通过在不危害安全的情况下通过防火墙使用rsync?
A:解答如下:

这通常有两种情况,一种是服务器在防火墙内,一种是服务器在防火墙外。
无论哪种情况,通常还是使用ssh,这时最好新建一个备份用户,并且配置sshd仅允许这个用户通过RSA认证方式进入。
如果服务器在防火墙内,则最好限定客户端的IP地址,拒绝其它所有连接。
如果客户机在防火墙内,则可以简单允许防火墙打开TCP端口22的ssh外发连接就ok了。

Q:我能将更改过或者删除的文件也备份上来吗?
A:当然可以:

你可以使用如:rsync -other -options -backupdir = ./backup-2000-2-13  ...这样的命令来实现。
这样如果源文件:/path/to/some/file.c改变了,那么旧的文件就会被移到./backup-2000-2-13/path/to/some/file.c,这里这个目录需要自己

手工建立起来

Q:我需要在防火墙上开放哪些端口以适应rsync?
A:视情况而定

rsync可以直接通过873端口的tcp连接传文件,也可以通过22端口的ssh来进行文件传递,但你也可以通过下列命令改变它的端口:

rsync --port 8730 otherhost::
或者
rsync -e 'ssh -p 2002' otherhost:

Q:我如何通过rsync只复制目录结构,忽略掉文件呢?
A:rsync -av --include '*/' --exclude '*' source-dir dest-dir

Q:为什么我总会出现"Read-only file system"的错误呢?
A:看看是否忘了设"read only = no"了

Q:为什么我会出现'@ERROR: invalid gid'的错误呢?
A:rsync使用时默认是用uid=nobody;gid=nobody来运行的,如果你的系统不存在nobody组的话,就会出现这样的错误,可以试试gid =

nogroup或者其它

Q:绑定端口873失败是怎么回事?
A:如果你不是以root权限运行这一守护进程的话,因为1024端口以下是特权端口,会出现这样的错误。你可以用--port参数来改变。

Q:为什么我认证失败?
A:从你的命令行看来:

你用的是:
> bash$ rsync -a 144.16.251.213::test test
> Password:
> @ERROR: auth failed on module test
>
> I dont understand this. Can somebody explain as to how to acomplish this.
> All suggestions are welcome.

应该是没有以你的用户名登陆导致的问题,试试rsync -a max@144.16.251.213::test test

四、一些可借鉴的脚本

这里这些脚本都是rsync网站上的例子:

1、每隔七天将数据往中心服务器做增量备份

#!/bin/sh

# This script does personal backups to a rsync backup server. You will end up
# with a 7 day rotating incremental backup. The incrementals will go
# into subdirectories named after the day of the week, and the current
# full backup goes into a directory called "current"
# tridge@linuxcare.com

# directory to backup
BDIR=/home/$USER

# excludes file - this contains a wildcard pattern per line of files to exclude
EXCLUDES=$HOME/cron/excludes

# the name of the backup machine
BSERVER=owl

# your password on the backup server
export RSYNC_PASSWORD=XXXXXX


########################################################################

BACKUPDIR=`date +%A`
OPTS="--force --ignore-errors --delete-excluded --exclude-from=$EXCLUDES
      --delete --backup --backup-dir=/$BACKUPDIR -a"

export PATH=$PATH:/bin:/usr/bin:/usr/local/bin

# the following line clears the last weeks incremental directory
[ -d $HOME/emptydir ] || mkdir $HOME/emptydir
rsync --delete -a $HOME/emptydir/ $BSERVER::$USER/$BACKUPDIR/
rmdir $HOME/emptydir

# now the actual transfer
rsync $OPTS $BDIR $BSERVER::$USER/current

2、备份至一个空闲的硬盘

#!/bin/sh

export PATH=/usr/local/bin:/usr/bin:/bin

LIST="rootfs usr data data2"

for d in $LIST; do
    mount /backup/$d
    rsync -ax --exclude fstab --delete /$d/ /backup/$d/
    umount /backup/$d
done

DAY=`date "+%A"`
    
rsync -a --delete /usr/local/apache /data2/backups/$DAY
rsync -a --delete /data/solid /data2/backups/$DAY

3、对vger.rutgers.edu的cvs树进行镜像

#!/bin/bash

cd /var/www/cvs/vger/
PATH=/usr/local/bin:/usr/freeware/bin:/usr/bin:/bin

RUN=`lps x | grep rsync | grep -v grep | wc -l`
if [ "$RUN" -gt 0 ]; then
    echo already running
    exit 1
fi

rsync -az vger.rutgers.edu::cvs/CVSROOT/ChangeLog $HOME/ChangeLog

sum1=`sum $HOME/ChangeLog`
sum2=`sum /var/www/cvs/vger/CVSROOT/ChangeLog`

if [ "$sum1" = "$sum2" ]; then
    echo nothing to do
    exit 0
fi

rsync -az --delete --force vger.rutgers.edu::cvs/ /var/www/cvs/vger/
exit 0

4、利用find的一种巧妙方式

rsync -avR remote:'`find /home -name "*.[ch]"`' /tmp/

可以用这种方法列出需要备份的文件列表——这种方法似乎比较少人用到。

五、参考资料:

1、http://rsync.samba.org/
2、rsync examples
3、rsync FAQ

- 作者: colding 2006年06月27日, 星期二 10:49  回复(0) |  引用(0) 加入博采

用C语言进行CGI程序设计
一、CGI概述
   CGI(公用网关接口)规定了Web服务器调用其他可执行程序(CGI程 序)的接口协议标准。Web服务器通过调用CGI程序实现和Web浏览器的交互,也就是CGI程序接受Web浏览器发送给Web服务器的信息,进行处理, 将响应结果再回送给Web服务器及Web浏览器。CGI程序一般完成Web网页中表单(Form)数据的处理、数据库查询和实现与传统应用系统的集成等工 作。CGI程序可以用任何程序设计语言编写,如Shell脚本语言、Perl、Fortran、Pascal、C语言等。但是用C语言编写的CGI程序具 有执行速度快、安全性高(因为C语言程序是编译执行且不被修改)等特点。

   CGI接口标准包括标准输入、环境变量、标准输出三部分。

   1.标准输入

   CGI程序像其他可执行程序一样,可通过标准输入(stdin)从Web服务器得到输入信息,如Form中的数据,这就是所谓的向CGI程序传递数据的 POST方法。这意味着在操作系统命令行状态可执行CGI程序,对CGI程序进行调试。POST方法是常用的方法,本文将以此方法为例,分析CGI程序设 计的方法、过程和技巧。

   2.环境变量

   操作系统提供了许多环境变量,它们定义了程序的执行环境,应用程序可以存取它们。Web服务器和CGI接口又另外设置了自己的一些环境变量,用来向CGI 程序传递一些重要的参数。CGI的GET方法还通过 环境变量QUERY-STRING向CGI程序传递Form中的数据。

   3.标准输出

   CGI程序通过标准输出(stdout)将输出信息传送给Web服务器。传送给Web服务器的信息可以用各种格式,通常是以纯文本或者HTML文本的形式,这样我们就可以在命令行状态调试CGI程序,并且得到它们的输出。

   下面是一个简单的CGI程序,它将HTML中Form的信息直接输出到We b浏览器。
   #include <stdio.h>
   #include <stdib.h>
   main()
   {
    int,i,n;
   printf (″Contenttype:text/plainnn″);
   n=0;
   if(getenv(″CONTENT-LENGTH″))
   n=atoi(getenv(CONTENT-LENGTH″));
   for (i=0;i<n;i++)
   putchar(getchar());
   putchar (′n′);
   fflush(stdout);
   }


   下面对此程序作一下简要的分析。
   prinft (″Contenttype:text/plainnn″);
   此行通过标准输出将字符串″Contenttype:text/plainnn″传送给Web服务器。它是一个MIME头信息,它告诉Web服务器随后的 输出是以纯ASCII文本的形式。请注意在这个头信息中有两个新行符,这是因为Web服务器需要在实际的文本信息开始之前先看见一个空行。
   if (getenv(″CONTENT-LENGTH″))
   n=atoi (getenv(″CONTENT-LENGTH″));
   此行首先检查环境变量CONTENT-LENGTH是否存在。Web服务器在调用使用POST方法的CGI程序时设置此环境变量,它的文本值表示Web服 务器传送给CGI程序的输入中的字符数目,因此我们使用函数atoi() 将此环境变量的值转换成整数,并赋给变量n。请注意Web服务器并不以文件结束符来终止它的输出,所以如果不检查环境变量CONTENT-LENGTH, CGI程序就无法知道什么时候输入结束了。


   for (i=0;i<n;i++)
   putchar(getchar());
   此行从0循环到(CONTENT-LENGTH-1)次将标准输入中读到的每一个字符直接拷贝到标准输出,也就是将所有的输入以ASCII的形式回送给Web服务器。
   通过此例,我们可将CGI程序的一般工作过程总结为如下几点。
   1.通过检查环境变量CONTENT-LENGTH,确定有多少输入;
   2.循环使用getchar()或者其他文件读函数得到所有的输入;
   3.以相应的方法处理输入;
   4.通过″Contenttype:″头信息,将输出信息的格式告诉Web服务器;
   5.通过使用printf()或者putchar()或者其他的文件写函数,将输出传送给Web服务器。
   总之,CGI程序的主要任务就是从Web服务器得到输入信息,进行处理,然后将输出结果再送回给Web服务器。

   二、环境变量

   环境变量是文本串(名字/值对),可以被OS Shell或其他程序设置 ,也可以被其他程序访问。它们是Web服务器传递数据给CGI程序的简单手段,之所以称为环境变量是因为它们是全局变量,任何程序都可以存取它们。

   下面是CGI程序设计中常常要用到的一些环境变量。
   HTTP-REFERER:调用该CGI程序的网页的URL。
   REMOTE-HOST:调用该CGI程序的Web浏览器的机器名和域名。
   REQUEST-METHOD:指的是当Web服务器传递数据给CGI程序时所采用的方法,分为GET和POST两种方法。GET方法仅通过环境变量(如 QUERY-STRING)传递数据给CGI程序,而POST方法通过环境变量和标准输入传递数据给CGI程序,因此POST方法可较方便地传递较多的数 据给CGI程序。

   SCRIPT-NAME:该CGI程序的名称。
   QUERY-STRING:当使用POST方法时,Form中的数据最后放在QUERY-STRING中,传递给CGI程序。
   CONTENT-TYPE:传递给CGI程序数据的MIME类型,通常为″applica tion/x-www-form-url encodede″,它是从HTML Form中以POST方法传递数据给CGI程序的数据编码类型,称为URL编码类型。
   CONTENT-LENGTH:传递给CGI程序的数据字符数(字节数)。
   在C语言程序中,要访向环境变量,可使用getenv()库函数。例如:
   if (getenv (″CONTENT-LENGTH″))
    n=atoi(getenv (″CONTENT-LENGTH″));
   请注意程序中最好调用两次getenv():第一次检查是否存在该环境变量,第二次再使用该环境变量。这是因为函数getenv()在给定的环境变量名不 存在时,返回一个NULL(空)指针,如果你不首先检查而直接引用它,当该环境变量不存在时会引起CGI程序崩溃。

   三、From输入的分析和解码

   1.分析名字/值对

   当用户提交一个HTML Form时,Web浏览器首先对Form中的数据以名字/值对的形式进行编码,并发送给Web服务器,然后由Web服务器传递给CGI程序。其格式如下:
   name1=value1&name2=value2&name3=value3&name4=value4&...
   其中名字是Form中定义的INPUT、SELECT或TEXTAREA等标置(Tag)名字,值是用户输入或选择的标置值。这种格式即为URL编码,程 序中需要对其进行分析和解码。要分析这种数据流,CGI程序必须首先将数据流分解成一组组的名字/值对。这可以通过在输入流中查找下面的两个字符来完成。
   每当找到字符=,标志着一个Form变量名字的结束;每当找到字符& ,标志着一个Form变量值的结束。请注意输入数据的最后一个变量的值不以&结束。
   一旦名字/值对分解后,还必须将输入中的一些特殊字符转换成相应的ASCII字符。这些特殊字符是:
   +:将+转换成空格符;
   %xx:用其十六进制ASCII码值表示的特殊字符。根据值xx将其转换成相应的ASCII字符。
   对Form变量名和变量值都要进行这种转换。下面是一个对Form数据进行分析并将结果回送给Web服务器的CGI程序。


   #include <stdio.h>
   #include <stdlib.h>
   #include <strings.h>
   int htoi(char *);
   main()
   {
    int i,n;
   char c;
   printf (″Contenttype: text/plainnn″);
   n=0;
   if (getenv(″CONTENT-LENGTH″))
    n=atoi(getenv(″CONTENT-LENGTH″));
   for (i=0; i<n;i++){
    int is-eq=0;
   c=getchar();
   switch (c){
    case ′&′:
     c=′n′;
     break;
    case ′+′:
     c=′ ′;
     break;
    case ′%′:{
     char s[3];
     s[0]=getchar();
     s[1]=getchar();
     s[2]=0;
     c=htoi(s);
     i+=2;
    }
    break;
   case ′=′:
    c=′:′;
    is-eq=1;
    break;
   };
   putchar(c);
   if (is-eq) putchar(′ ′);
   }
   putchar (′n′);
   fflush(stdout);
   }
   /* convert hex string to int */
   int htoi(char *s)
   {
    char *digits=″0123456789ABCDEF″;
   if (islower (s[0])) s[0]=toupper(s[0]);
   if (islower (s[1])) s[1]=toupper(s[1]);
   return 16 * (strchr(digits, s[0]) -strchr (digits,′0′)
)
   +(strchr(digits,s[1])-strchr(digits,′0′));
   }

   上面的程序首先输出一个MIME头信息给Web服务器,检查输入中的字符数,并循环检查每一个字符。当发现字符为&时,意味着一个名字/值对的结 束,程序输出一个空行;当发现字符为+时,将它转换成空格; 当发现字符为%时,意味着一个两字符的十六进制值的开始,调用htoi()函数将随后的两个字符转换为相应的ASCII字符;当发现字符为=时,意味着一 个名字/值对的名字部分的结束,并将它转换成字符:。最后将转换后的字符输出给Web服务器。
   四、产生HTML输出

   CGI程序产生的输出由两部分组成:MIME头信息和实际的信息。两部分之间以一个空行分开。我们已经看到怎样使用MIME头信息″Cont enttype:text/plainnn″和printf()、put char()等函数调用来输 出纯ASCII文本给Web服务器。实际上,我们也可以使用MIME头信息″C ontenttype:text/htmlnn″来输出HTML源代码给Web服务器。请注意任何MIME头信息后必须有一个空行。一旦发送这个MIME 头信息给We b服务器后,Web浏览器将认为随后的文本输出为HTML源代码,在HTML源代码中可以使用任何HTML结构,如超链、图像、Form,及对其他CGI 程 序的调用。也就是说,我们可以在CGI程序中动态产生HTML源代码输出 ,下面是一个简单的例子。

  #include <stdio.h>
   #include <string.h>
   main()
   {
    printf(″Contenttype:text/htmlnn″);
   printf(″<html>n″);
   printf(″<head><title>An HTML Page From a CGI</title></h ead>n″);
   printf(″<body>
n″);
printf(″<h2> This is an HTML page generated from with i n a CGI program..   .</h2>n″);
   printf(″<hr><p>n″);
   printf(″<a href="../output.html#two"><b> Go back to out put.html page <
   /b></a>n″);
   printf(″</body>n″);
   printf(″</html>n″);
   fflush(stdout);
   }


   上面的CGI程序简单地用printf()函数来产生HTML源代码。请注意在输出的字符串中如果有双引号,在其前面必须有一个后斜字符, 这是因为整个HTML代码串已经在双引号内,所以HTML代码串中的双引号符必须用一个后斜字符来转义。





在HTML中,当客户填写了表单,并按下了发送(submit)按钮后,表单的内容被发送到了服务器端,一般的,这时就需要有一个服务器端脚本来对表单的 内容进行一些处理,或者是把它们保存起来,或者是按内容进行一些查询,或者是一些别的什么。没有了CGI,WEB的世界就完全失去了它的交互性,所有的信 息都变成单向的了,而不能够有任何的反馈。


   有的人认为可以用java script来代替CGI程序,这其实是一个概念上的错误。java script只能够在客户浏览器中运行,而CGI却是工作在服务器上的。他们所做的工作有一些交集,比如表单数据验证一类的,但是java script是绝对无法取代CGI的。但可以这样说,如果一项工作即能够用java script来做,又可以用CGI来做,那么绝对要使用java script,在执行的速度上,java script比CGI有着先天的优势。只有那些在客户端解决不了的问题,比如和某个远程数据库交互,这时就应该使用CGI了。


   简单的说来,CGI是用来沟通HTML表单和服务器端程序的接口(interface)。说它是接口,也就是说CGI并不是一种语言,而是可以被其他语言 所应用的一个规范集。理论上讲,你可以用任何的程序语言来编写CGI程序,只要在编程的时候符合CGI规范所定义的一些东西就可以了。由于C语言在平台无 关性上表现不错(几乎在任何的系统平台下都有其相应编译器),而且对大多数程序员而言都算得上很熟悉(不像Perl),因此,C是CGI编程的首选语言之 一。这儿我们介绍的,就是如何使用C来编写CGI程序。


   作为CGI编程的最为简单的例子,就是进行表单的处理。因而在这篇文章中,我们主要介绍的就是如何用C来编写CGI程序来进行表但处理。

   GET表单的处理

   对于那些使用了属性“METHOD=GET”的表单(或者没有METHOD属性,这时候GET是其缺省值),CGI定义为:当表单被发送到服务器断后,表 单中的数据被保存在服务器上一个叫做QUERY_STRING的环境变量中。这种表单的处理相对简单,只要读取环境变量就可以了。这一点对不同的语言有不 同的做法。在C语言中,你可以用库函数getenv(定义在标准库函数stdlib中)来把环境变量的值作为一个字符串来存取。你可以在取得了字符串中的 数据后,运用一些小技巧进行类型的转换,这都是比较简单的了。在CGI程序中的标准输出(output)(比如在C中的stdout文件流)也是经过重定 义了的。它并没有在服务器上产生任何的输出内容,而是被重定向到客户浏览器。这样,如果编写一个C的CGI程序的时候,把一个HTML文档输出到它的 stdout上,这个HTML文档会被在客户端的浏览器中显示出来。这也是CGI程序的一个基本原理。

   我们来看看具体的程序实现,下面是一段HTML表单:

< form ACTION="/cgi-bin/mult.cgi" >

< P >请在下面填入乘数和被乘数,按下确定后可以看到结果。

< INPUT NAME="m" SIZE="5" >

< INPUT NAME="n" SIZE="5" >< BR >

< INPUT TYPE="SUBMIT" values="确定" >

< /form >


   我们要实现的功能很简单,就是把表单中输入的数值乘起来,然后输出结果。其实这个功能完全可以用java script来实现,但为了让程序尽量的简单易懂,我还是选择了这个小小的乘法来作为示例。


   下面就是处理这个表单的CGI程序,对应于form标签中的ACTION属性值。


#include < stdio.h >

#include < stdlib.h >

int main(void)

{

char *data;

long m,n;

printf("%s%c%c ","Content-Type:text/html;charset=gb2312",13,10);

printf("< TITLE >乘法结果< /TITLE > ");

printf("< H3 >乘法结果< /H3 > ");

data = getenv("QUERY_STRING");

if(data == NULL)

printf("< P >错误!数据没有被输入或者数据传输有问题");

else if(sscanf(data,"m=%ld&n=%ld",&m,&n)!=2)

printf("< P >错误!输入数据非法。表单中输入的必须是数字。");

else

printf("< P >%ld和%ld的成绩是:%ld。",m,n,m*n);

return 0;

}


   具体的C语法就不多讲了,我们来看看它作为CGI程序所特殊的地方。


   前面已经提到标准输出的内容就是要被显示在浏览器中的内容。第一行的输出内容是必须的,也是一个CGI程序所特有的:printf("%s%c%c ","Content-Type:text/html",13,10),这个输出是作为HTML的文件头。因为CGI不仅可以像浏览器输出HTML文本, 而且可以输出图像,声音之类的东西。这一行告诉浏览器如何处理接受到的内容。在Content-Type的定义后面跟有两行的空行,这也是不可缺少的。因 为所有CGI程序的头部输出都是相近的,因而可以为其定义一个函数,来节省编程的时间。这是CGI编程常用的一个技巧。


   程序在后面调用了用了库函数getevn来得到QUERY_STRING的内容,然后使用sscanf函数把每个参数值取出来,要注意的是sscanf函数的用法。其他的就没有什么了,和一般的C程序没有区别。


   把程序编译后,改名为mult.cgi放在/cgi-bin/目录下面,就可以被表单调用了。这样,一个处理GET方式表单的CGI程序就大功告成了。


   POST表单处理


   下面我们来考虑另外一种表单传送方法:POST。假设我们要实现的任务是这样的:把表单中客户输入的一段文本内容添加到服务器上的一个文本文件的后面。这 可以看作是一个留言版程序的雏形。显然,这个工作是无法用java script这种客户端脚本来实现,也算得上真正意义上的CGI程序了。


   看起来这个问题和上面讲的内容很相近,仅仅是用不同的表单和不同的脚本(程序)而已。但实际上,这中间是有一些区别的。在上面的例子中,GET的处理方法 可以看作是“纯查询(pure query)”类型的,也就是说,它与状态无关。同样的数据可以被提交任意的次数,而不会引起任何的问题(除了服务器的一些小小的开销)。但是现在的任务 就不同了,至少它要改变一个文件的内容。因而,可以说它是与状态有关的。这也算是POST和GET的区别之一。而且,GET对于表单的长度是有限制的,而 POST则不然,这也是在这个任务中选用POST方法的主要原因。但相对的,对GET的处理速度就要比POST快一些。


   在CGI的定义中,对于POST类型的表单,其内容被送到CGI程序的标准输入(在C语言中是stdin),而被传送的长度被放在环境变量 CONTENT_LENGTH中。因而我们要做的就是,在标准输入中读入CONTENT_LENGTH长度的字符串。从标准输出读入数据听起来似乎要比从 环境变量中读数据来的要容易一些,其实则不然,有一些细节地方要注意,这在下面的程序中可以看到。特别要注意的一点就是:CGI程序和一般的程序有所不 同,一般的程序在读完了一个文件流的内容之后,会得到一个EOF的标志。但在CGI程序的表单处理过程中,EOF是永远不会出现的,所以千万不要读多于 CONTENT_LENGTH长度的字符,否这会有什么后果,谁也不知道(CGI规范中没有定义,一般根据服务器不同而有不同得处理方法)。


   我们来看看到底如何从POST表单收集数据到CGI程序,下面給出了一個比较简单的C源代碼:

#include < stdio.h >

#include < stdlib.h >

#define MAXLEN 80

#define EXTRA 5

/* 4个字节留给字段的名字"data", 1个字节留给"=" */

#define MAXINPUT MAXLEN+EXTRA+2

/* 1个字节留给换行符,还有一个留给后面的NULL */

#define DATAFILE "../data/data.txt"

/* 要被添加数据的文件 */


void unencode(char *src, char *last, char *dest)

{

for(; src != last src++, dest++)

if(*src == "+")

*dest = " ";

else if(*src == "%") {

int code;

if(sscanf(src+1, "%2x", &code) != 1) code = "?";

*dest = code;

src +=2; }

else

*dest = *src;

*dest = " ";

*++dest = "";

}

int main(void)

{

char *lenstr;

char input[MAXINPUT], data[MAXINPUT];

long len;

printf("%s%c%c ",

"Content-Type:text/html;charset=gb2312",13,10);

printf("< TITLE >Response< /TITLE > ");

lenstr = getenv("CONTENT_LENGTH");

if(lenstr == NULL || sscanf(lenstr,"%ld",&len)!=1 || len > MAXLEN)

printf("< P >表单提交错误");

else {

FILE *f;

fgets(input, len+1, stdin);

unencode(input+EXTRA, input+len, data);

f = fopen(DATAFILE, "a");

if(f == NULL)

printf("< P >对不起,意外错误,不能够保存你的数据 ");

else

fputs(data, f);

fclose(f);

printf("< P >非常感谢,您的数据已经被保存< BR >%s",data);

}

return 0;

}




   从本质上来看,程序先从CONTENT_LENGTH环境变量中得到数据的字长,然后读取相应长度的字符串。因为数据内容在传输的过程中是经过了编码的,所以必须进行相应的解码。编码的规则很简单,主要的有这几条:


   1. 表单中每个每个字段用字段名后跟等号,再接上上这个字段的值来表示,每个字段之间的内容用&连结;


   2. 所有的空格符号用加号代替,所以在编码码段中出现空格是非法的;


   3. 特殊的字符比如标点符号,和一些有特定意义的字符如“+”,用百分号后跟其对应的ACSII码值来表示。


   例如:如果用户输入的是:


   Hello there!


   那么数据传送到服务器的时候经过编码,就变成了data=Hello+there%21 上面的unencode()函数就是用来把编码后的数据进行解码的。在解码完成后,数据被添加到data.txt文件的尾部,并在浏览其中回显出来。


   把文件编译完成后,把它改名为collect.cgi后放在CGI目录中就可以被表单调用了。下面给出了其相应的表单:


< form ACTION="/cgi-bin/collect.cgi" METHOD="POST" >

< P >请输入您的留言(最多80个字符):< BR >< INPUT NAME="data" SIZE="60" MAXLENGTH="80" >< BR >

< INPUT TYPE="SUBMIT" values="确定" >

< /form >


   事实上,这个程序只能作为例子,是不能够正式的使用的。它漏掉了很关键的一个问题:当有多个用户同时像文件写入数据是,肯定会有错误发生。而对于一个这样 的程序而言,文件被同时写入的几率是很大的。因此,在比较正式的留言版程序中,都需要做一些更多的考虑,比如加入一个信号量,或者是借助于一个钥匙文件 等。因为那只是编程的技巧问题,在这儿就不多说了。


   最后,我们来写一个浏览data.txt文件的的CGI程序,这只需要把内容输出到stdout就可以了:

   #include < stdio.h >

   #include < stdlib.h >

   #define DATAFILE "../data/data.txt"

   int main(void)

   {

   FILE *f = fopen(DATAFILE,"r");

   int ch;

   if(f == NULL) {

   printf("%s%c%c ",

   "Content-Type:text/html;charset=gb2312",13,10);

   printf("< TITLE >错误 < /TITLE > ");

   printf("< P >< EM >意外错误,无法打开文件< /EM >"); }

   else {

   printf("%s%c%c ",

   "Content-Type:text/plain",13,10);

   while((ch=getc(f)) != EOF)

   putchar(ch);

   fclose(f); }

   return 0;

   }


   这个程序唯一要注意的是:它并没有把data.txt 包装成HTML格式后再输出,而是直接作为简单文本(plain text)输出,这只要在输出的头部用text/plain类型代替text/html就可以了,浏览器会根据Content-Type的类型自动的选择 相应的处理方法。


   要触发这个程序也很简单,因为没有数据要输入,所以只需一个按钮就可以搞定了:


   < form ACTION="/cgi-bin/viewdata.cgi" >

   < P >< INPUT TYPE="SUBMIT" values="察看" >

   < /form >


   到这儿,一些基本的用C编写CGI程序的原理就将完了。当然,就凭讲的这些内容,还很难编写出一个好的CGI程序,这需要进一步的学习CGI的规范定义,以及一些其他的CGI编程特有的技巧。


   这篇文章的目的,也就是要你了解一下CGI编程的概念。事实上,现在的一些主流的服务器端脚本编程语言如ASP,PHP,JSP等,都基本上具备了CGI 编程的大部分的功能,但他们在使用上的,确实是比无论用什么语言进行CGI编程都要容易的多。所以在进行服务器端编程的时候,一般都会首先考虑使用这些脚 本编程语言。只有当他们也解决不了,比如要进行一些更为底层的编程的时候,才会用到CGI。

- 作者: colding 2006年06月22日, 星期四 10:54  回复(1) |  引用(0) 加入博采

Linux 守护进程的编程方法
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进 程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任 务。比如,作业规划进程crond,打印进程lpd等。

守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同Unix环境下守护进程的编程规则并不一致。这需要读者注意,照搬 某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。下面将全面介绍Linux下守护进程的编程要点并给出详细实例。

一. 守护进程及其特性

守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的 文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。最后,守 护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端 (通常是shell)执行。

总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果读者对进程有比较深入的认识就更容易理解和编程了。

二. 守护进程的编程要点

前面讲过,不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样,区别在于具体的实现细节不同。这个原则就是要满足守护 进程的特性。同时,Linux是基于Syetem V的SVR4并遵循Posix标准,实现起来与BSD4相比更方便。编程要点如下;

1. 在后台运行。

为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。

if(pid=fork())

exit(0);//是父进程,结束父进程,子进程继续

2. 脱离控制终端,登录会话和进程组

有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。

控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
setsid();

说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。

3. 禁止进程重新打开控制终端

现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:

if(pid=fork())

exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)

4. 关闭打开的文件描述符

进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:

for(i=0;i 关闭打开的文件描述符close(i);>

5. 改变当前工作目录

进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")

6. 重设文件创建掩模

进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);

7. 处理SIGCHLD信号

处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为 僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。

signal(SIGCHLD,SIG_IGN);

这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

三. 守护进程实例

守护进程实例包括两部分:主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。

1. init.c清单

#include < unistd.h >
#include < signal.h >
#include < sys/param.h >
#include < sys/types.h >
#include < sys/stat.h >

void init_daemon(void)
{
int pid;
int i;

if(pid=fork())
exit(0);//是父进程,结束父进程
else if(pid< 0)
exit(1);//fork失败,退出
//是第一子进程,后台继续执行

setsid();//第一子进程成为新的会话组长和进程组长
//并与控制终端分离
if(pid=fork())
exit(0);//是第一子进程,结束第一子进程
else if(pid< 0)
exit(1);//fork失败,退出
//是第二子进程,继续
//第二子进程不再是会话组长

for(i=0;i< NOFILE;++i)//关闭打开的文件描述符
close(i);
chdir("/tmp");//改变工作目录到/tmp
umask(0);//重设文件创建掩模
return;
}
2. test.c清单
#include < stdio.h >
#include < time.h >

void init_daemon(void);//守护进程初始化函数

main()
{
FILE *fp;
time_t t;
init_daemon();//初始化为Daemon

while(1)//每隔一分钟向test.log报告运行状态
{
sleep(60);//睡眠一分钟
if((fp=fopen("test.log","a")) >=0)
{
t=time(0);
fprintf(fp,"I'm here at %sn",asctime(localtime(&t)) );
fclose(fp);
}
}
}
以上程序在RedHat Linux6.0下编译通过。步骤如下:
编译:gcc –g –o test init.c test.c
执行:./test
查看进程:ps –ef
从输出可以发现test守护进程的各种特性满足上面的要求。

- 作者: colding 2006年06月22日, 星期四 10:52  回复(0) |  引用(0) 加入博采

一些c 常见技巧
/*砍掉字符串中所有空格*/
void CUT_ALL_SPC(char *s)
{
int i,n;
char d[10000];
n=0;
for(i=0;i
if(s[i]!=32)
{
d[n]=s[i];
n++;
}
d[n]=0;
strcpy(s,d);
}

/*砍掉字符串左边空格*/
void CUT_LEFT_SPACE(char *s)
{
int i,j,k=0;
i=strlen(s)+1;
for(j=0;j
for(k=0;j
}


/*砍掉字符串右边空格*/
void CUT_RIGHT_SPACE(char *s)
{
int i,j;
i=strlen(s)-1;
for(j=i;j>-1;j--) if (s[j]!=' ') break;
s[j+1]=0;
}

/*获取机器日期*/
int DATE(char *s,char type)
{
char dat[30];
int num;
struct tm *tblock;
time_t t;
t=time(NULL);
tblock=localtime(&t);
strcpy(dt,asctime(tblock));
strcpy(s,"");
switch(type)
{
case 'N':
num=(*tblock).tm_year+1900;
itoa(num,s,10);
break;
case 'Y':
num=(*tblock).tm_mon+1;
itoa(num,s,10);
beak;
case 'R':
num=(*tblock).tm_mday;
itoa(num,s,10);
break;
case 'S':
strcpy(dt,asctime(tblock));
MID(s,dt,12,8);
break;
}
}
return num;
}

/*成批拷贝文件*/
int CopyFile(char *sfile,char *dfile,int f2d,int barlong,int height,int x,int y)
{
int Copyfile(char *sf,char *df);
int MakeNdir(char *Dir);
char filename[200][13],d[40],s[40],s1[40];
struct ffblk ffblk;
int done,i,j,l,len;
i=0;
done=findfirst(sfile,&ffblk,0);
if (!done) strcpy(filename[i],ffblk.ff_name);
while(!done)
{
done=findnext(&ffblk);
if (!done)
{
i++;
strcpy(filename[i],ffblk.ff_name);
}
}
if (f2d)
{
Copyfile(sfile,dfile);
return 1;
}
strcpy(s,sfile);
l=strlen(sfile);
for(j=l-1;j>=0;j--)
if (s[j]=='')
{
s[j+1]=0;
break;
}

/*拷贝一个文件*/
int Copyfile(char *sf,char *df)
{
FILE *in,*out;
char ch;
in=0;
out=0;
if ((in=fopen(sf,"rb"))==NULL)
exit(0);
if ((out=fopen(df,"wb"))==NULL)
exit(0);
while(!feof(in))
{
ch=fgetc(in);
if (ferror(in)) return 0;
fputc(ch,out);
if (ferror(out)) return 0;
}
fclose(in);
fclose(out);
return 1;
}

/*建立目录*/
int MakeNdir(char *Dir)
{
int i,l,j;
char s[10][40];
j=0;
l=strlen(Dir);
for(i=0;i
if (Dir[i]=='')
{
LEFT(s[j],Dir,i);
j++;
}
strcpy(s[j],Dir);
for(i=0;i<=j;i++)
if (access(s[i],0)) mkdir(s[i]);
return 1;
}

/*得到目录*/
int GetDir(char *dirF,char dataK[][14])
{
struct ffblk ffblk;
int done;
int i;
i=0;
done=findfirst(dirF,&ffblk,FA_DIREC);
while(!done)
{
if (ffblk.ff_attrib==16||ffblk.ff_attrib==17)
{
strcpy(dataK[i],ffblk.ff_name);
strcat(dataK[i],"");
i++;
}
done=findnext(&ffblk);
}
return i;
}

/*得到文件名*/
int GetFile(char *dirF,char dataK[][14])
{
struct ffblk ffblk;
int done;
int i;
i=0;
done=findfirst(dirF,&ffblk,0);
while(!done)
{
strcpy(dataK[i],ffblk.ff_name);
done=findnext(&ffblk);
i++;
}
return i;
}

- 作者: colding 2006年06月22日, 星期四 10:50  回复(0) |  引用(0) 加入博采

c语言库函数syslog--将信息记录至系统日志文件
参见 《unix 环境高级编程》第13 章 精灵进程

在Linux下有个syslogd的Daemon程式,syslog是个系统管理员必看的档案。因此,如果您的程式有除错或安全讯息要显示,写到syslog是个很好的选择。

syslog有三个函数,使用上,一般您只需要用syslog(...)这个函数即可,一般使用状况下,openlog/closelog是可有可无的。

syslog()中的priority是facility及level的组合,其後参数的用法与printf无异。

范例:
#include
#include
#include
#include

void main(void)
{
if (fork()==0) {
for (;;) {
syslog(LOG_USER|LOG_INFO,"syslog programming test\n");
sleep(10);
}
}
}

检验:
tail -f /var/log/messages

Mar 22 01:42:51 foxman log: syslog programming test
Mar 22 01:43:31 foxman last message repeated 4 times
Mar 22 01:44:31 foxman last message repeated 6 times
Mar 22 01:45:31 foxman last message repeated 6 times
Mar 22 01:46:21 foxman last message repeated 5 times



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


void openlog( char *ident, int option, int facility)
void syslog( int priority, char *format, ...)
void closelog( void )

option
用於openlog()的option参数可以是以下几个的组合:

LOG_CONS : 如果送到system logger时发生问题,直接写入系统console。
LOG_NDELAY : 立即开启连接(通常,连接是在第一次写入讯息时才打开的)。
LOG_PERROR : 将讯息也同时送到stderr
LOG_PID : 将PID含入所有讯息中

facility
facility参数用来指定何种程式在记录讯息,这可让设定档来设定何种讯息如何处理。

LOG_AUTH : 安全/授权讯息(别用这个,请改用LOG_AUTHPRIV)
LOG_AUTHPRIV : 安全/授权讯息
LOG_CRON : 时间守护神专用(cron及at)
LOG_DAEMON : 其它系统守护神
LOG_KERN : 核心讯息
LOG_LOCAL0到LOG_LOCAL7 : 保留
LOG_LPR : line printer次系统
LOG_MAIL : mail次系统
LOG_NEWS : USENET news次系统
LOG_SYSLOG : syslogd内部所产生的讯息
LOG_USER(default) : 一般使用者等级讯息
LOG_UUCP : UUCP次系统

level
决定讯息的重要性. 以下的等级重要性逐次递减:

LOG_EMERG : 系统无法使用
LOG_ALERT : 必须要立即采取反应行动
LOG_CRIT : 重要状况发生
LOG_ERR : 错误状况发生
LOG_WARNING : 警告状况发生
LOG_NOTICE : 一般状况,但也是重要状况
LOG_INFO : 资讯讯息
LOG_DEBUG : 除错讯

配置文件:
/etc/syslog-ng/syslog-ng.conf

添加语句为:
destination chentest_log { file("/var/log/chentest.log"); };
filter chentest { program("chentest"); };
filter n_chentest { not program("chentest"); };
log { source(src); filter(chentest); destination(chentest_log); };
log { source(src); filter(n_indexfiles); filter(n_chentest); filter(n_contentserver); filter(n_gongtest);destination(messages); };
第一句指明了写文件路径,
2句是一个过滤,表明运行程序chentest的日志写入/var/log/chentest.log里,当然最后要记得 定义非chentest的程序日志写入/var/log/messages里,否则LINUX仍然会写chentest的日志到message里面.
另外,注意:这样只定义了chentest这个应用程,必须确定你想写入你自己LOG的应用程序名必须是chentest.

完整的配置文件


# $Header: /var/cvsroot/gentoo-x86/app-admin/syslog-ng/files/syslog-ng.conf.gentoo,v 1.5 2005/05/12 05:46:10 mr_bones_ Exp $
#
# Syslog-ng default configuration file for Gentoo Linux
# contributed by Michael Sterrett

options {
    chain_hostnames(off);
    sync(0);

    # The default action of syslog-ng 1.6.0 is to log a STATS line
    # to the file every 10 minutes.  That's pretty ugly after a while.
    # Change it to every 12 hours so you get a nice daily update of
    # how many messages syslog-ng missed (0).
    stats(43200);
};

source src { unix-stream("/dev/log"); internal(); pipe("/proc/kmsg"); };

destination messages { file("/var/log/messages"); };
destination creatidf_log { file("/var/log/caocreatidf.log"); };
destination daily_log { file("/var/log/caodaily.log"); };
# By default messages are logged to tty12...
destination console_all { file("/dev/tty12"); };

filter creatidf { program("creatidf"); };
filter daily { program("daily"); };
filter n_creatidf { not program("creatidf"); };
filter n_daily { not program("daily"); };
# ...if you intend to use /dev/console for programs like xconsole
# you can comment out the destination line above that references /dev/tty12
# and uncomment the line below.
#destination console_all { file("/dev/console"); };

log { source(src); destination(messages); };
log { source(src); filter(creatidf); destination(creatidf_log); };
log { source(src); filter(daily); destination(daily_log); };
log { source(src); filter(n_creatidf); filter(n_daily); destination(messages); };
log { source(src); destination(console_all); };


http://www.linuxselfhelp.com/gnu/glibc/html_chapter/libc_18.html

- 作者: colding 2006年06月19日, 星期一 18:24  回复(0) |  引用(1) 加入博采

调用 动态链接库 示例
两种方法:
1 。  直接把动态度添加到系统默认库路径下 /usr/lib     在程序中包含头文件直接调用函数比较快捷
2。  采用dlopen调用

头文件likelihood.h     动态库liblikelihood.so
#ifndef _LIKELIHOOD_H
#define _LIKELIHOOD_H
extern "C" {
  long initial(const char *stopword, const char *conffile);       
  long learn(const char *libpath, const char *filename,const char *searchfile);
  long evaluate(const char *libpath, const char *filename);
  long release();
}
#endif

调用动态苦的程序
#include "likelihood.h"
#include "stdio.h"
#include "dlfcn.h"                                //包含此文件
#define SOFILE "liblikelihood.so"        //动态库 .so
#include <string>
#include <iostream>
using namespace std;
int main(int argc ,char *argv[])
{
    long (*initial) (const char *,const char *);        //函数声明
    long (*learn)(const char *, const char *,const char *);      //函数声明
    //long (*release)();
    string libpath = "/usr/nc-home/clusternew";
    string filename("/usr/nc-home/clusternew/cao.nat.txt");
    string searchfile("/usr/nc-home/clusternew/key.txt");
    string stopword("/usr/nc-home/clusternew/stopwords.txt");
    string conffile("/usr/nc-home/index/src/diskcustom_sort/index_so/index.conf");
    //learn(libpath.c_str(),filename.c_str(),searchfile.c_str(),stopword.c_str());
    void *dp;              //指针声明
    char *error;
     dp = dlopen(SOFILE,RTLD_LAZY);      //打开动态库
     if (dp==NULL)
     {
           fputs(dlerror(),stderr);
         exit(1);
     }
     *(void **) (&initial) = dlsym(dp,"initial");           //调用动态库的函数
     error = dlerror();
     if (error) /* ...... */
     {
         fputs(error,stderr);
         exit(1);
     }
     initial(stopword.c_str(),conffile.c_str());
     cout<<"init sucess"<<"\n";
    
     *(void **) (&learn) = dlsym(dp,"learn");
     error = dlerror(); 
     if (error) /* ...... */
     {
         fputs(error,stderr);
         exit(1);
     }
     for(int i =0 ;i< 3 ;i++)
     {
          learn(libpath.c_str(),filename.c_str(),searchfile.c_str());
        cout<<"deal "<< i << &uot;file\n";
     }
     cout<<"learn suceed"<<"\n";

     dlclose(dp);                                //关闭指针
    cout<<"finish\n";
    return 0;
     
               
}

 

- 作者: colding 2006年06月5日, 星期一 11:20  回复(0) |  引用(0) 加入博采

关于C++ 和 C 接口互相调用的问题 extern

关于接口互相调用的问题 首先讨论C++ 调用 C 的库

1。 C库 的头文件  .h

#ifndef LIBWORDSFQ
#define LIBWORDSFQ

typedef struct{
     char *words;
     int  wordsfq;
}wordsfqinfo;
#ifdef __cplusplus
extern "C"{
#endif
long Searchfq( int inputword_number, void * wordsfqlist  );
void Init( const char *conffilename );
void Release();
#ifdef __cplusplus

#endif
#endif

2。c 调用 C++库

.h 文件

#ifndef _LIKELIHOOD_H
#define _LIKELIHOOD_H
extern "C" {
  long learn(const char *libpath, const char *filename,const char *searchfile,const char *stopword);
  long evaluate(const char *libpath, const char *filename, const char *stopword);
}
#endif

具体情况参见

C++ 中extern “C”含义深层探索   一文

- 作者: colding 2006年06月1日, 星期四 09:56  回复(0) |  引用(0) 加入博采