Lyon

个人博客

授人以鱼,亦授人以渔.


Linux Shell编程笔记 第五部分 高级shell编程技巧

第五部分 高级shell编程技巧

第25章   深入讨论<<	
25.1   快速创建一个文件	
25.2   快速创建打印文档	
25.3   自动选择菜单	
25.4   自动ftp传输	
25.5   访问数据库	

第26章   shell 工具	
26.1   创建保存信息的文件	
26.1.1   使用date命令创建日志文件	
26.1.2   创建唯一的临时文件
26.2   信号	
26.2.1   杀死一个进程	
26.2.2   检测信号	
26.3   trap	
26.3.1   捕获信号并采取相应的行动	
26.3.2   捕获信号并采取行动的另一个例子	
26.3.3   锁住终端	
26.3.4   忽略信号	
26.4   eval	
26.4.1   执行含有字符串的命令	
26.4.2   给每个值一个变量名	
26.5   logger命令	
26.5.1   使用logger命令	
26.5.2   在脚本中使用logger命令	

第27章   几个脚本例子
27.1   pingall	
27.2   backup_gen	
27.3   del.lines	
27.4   access.deny	
27.5   logroll	
27.6   nfsdown	

第28章   运行级别脚本
28.1   怎么知道系统中是否含有运行级别目录	
28.2   确定当前的运行级别	
28.3   快速熟悉inittab	
28.4   运行级别	
28.4.1   各种运行级别	
28.4.2   运行级别脚本的格式
28.4.3   安装运行级别脚本	
28.5   使用inittab来启动应用程序	
28.6   启动和停止服务的其他方法	

第29章   cgi脚本	
29.1   什么是Web页面?	
29.2   cgi	
29.3   连接Web服务器	
29.4    cgi和HTM脚本	
29.4.1   基本cgi脚本	
29.4.2   显示shell命令输出	
29.4.3   使用SSI	
29.4.4   访问计数器	
29.4.5   使用一个链接来显示当前Web环境变量	
29.4.6   其他常用的环境变量	
29.5   get和post方法简介	
29.5.1   get方法	
29.5.2   post方法	
29.5.3   填充列表项	
29.5.4   自动刷新页面

第25章 深入讨论«

  • 快速创建一个文件。
  • 自动进入菜单。
  • ftp 传输。
  • 连接至其他应用系统。

这里再简要回顾一下«的用法。当shell看到«的时候,它就会知道下一个词是一个分界符。在该分界符以后的内容都被当作输入,直到shell又看到该分界符(位于单独的一行)。这个分界符可以是你所定义的任何字符串。 可以使用 «来创建文件、显示文件列表、排序文件列表以及创建屏幕输入。

25.1 快速创建一个文件

可以使用这种方法快速创建一个文件,并向其中存入一些文本:

$ cat >> myfile <<NEWFILE 

现在可以输入一些文本,结束时只要在新的一行键入NEWFILE即可,这样就创建了一个名为myfile的文件,该文件中包含了一些文本。 如果打开了一个已经存在的文件,输入的内容会附加到该文件的末尾。 如果使用 tab键,注意,一些老版本的shell可能无法正确理解它的含义。为了解决这一问题,可以在«之后加一个横杠-,就像下面这样:

cat >> myfile <<- NEWFILE 
...

25.2 快速创建打印文档

假如希望打印一小段信息,可以采用这种方法而不必使用vi编辑器。在本例中,一旦在输入QUICKDOC之后按回车键,相应的文档就会被送到打印机。

$ lpr<<QUICKDOC

25.3 自动选择菜单

不但可以很方便地使用«创建菜单屏幕,还可以使用它来自动选择菜单,而不是由用户手工进行选择。 我编写了一个菜单驱动的数据库管理脚本,可以使用它来完成备份和其他系统管理任务。 该脚本本来是在白天由用户来运行的,现在决定把这些工作交给 cron夜间完成,我不想再另 外写一个自动运行的脚本,于是我使用«中的输入来选择 syb_backup脚本的菜单选项。

25.4 自动ftp传输

«的另外一个流行的应用就是自动ftp传输。在使用ftp时,如果能够向用户提供一个简单的界面就好了。下面的脚本使用了匿名用户anonymous建立了一个ftp连接。 这是一个特殊的用户,它使得系统能够创建一个含有公共目录的安全帐户。 一般来说,所有以匿名用户身份进行连接的用户都只能从公共目录中下载文件,不过只要权限允许,用户也可以上载。 匿名用户的口令可以是任何字符串,不过最好使用主机名加上本地用户名,或电子邮件地址。 下面的脚本将会提示如下的信息:

  • 1) 希望登录的远程主机。
  • 2) 文件传输的类型是二进制方式还是 ASCII方式。
  • 3) 要下载的文件名。
  • 4) 存放下载文件的本地目录。

当用户输入想要连接的主机之后,首先执行一个名为traceroute的脚本验证本地主机是否能够连接到远程主机。如果traceroute执行失败,这个自动ftp传输的脚本将会再次提示用户输入主机名。

用户在看到传输模式选择的提示之后按回车键,将会选择缺省的二进制模式。用户在输入所要下载的文件名之后,将会被提示输入保存下载文件的本地目录。缺省的本地目录是/tmp。如果用户所给出的目录无法找到,仍将使用缺省的/tmp目录。下载文件在本地的文件名将是原文件名加上 .ftp后缀。最后,用户所有的选择都将在屏幕上显示出来,待用户确认后开始进行传输。

25.5 访问数据库

shell脚本一个常用的用途就是访问数据库系统获得信息。实现这样的功能, «是再理想不过了。可以用它来输入你在面对数据库提示时所做的各种选择。下面的例子并不是数据库中的一个练习,而是为了用来介绍如何使用«来连接其他应用程序,完成相应的任务。 对于某一个数据库系统来说,在使用某种第三方产品进行访问时, select into功能将会被 关闭。这意味着该数据库不能被用来插入数据或创建临时表。 为了解决这个问题,我们使用 «进行数据库连接,并使用一个 for循环来提供各个数据库名,一旦连接成功, «将用来向 sql命令提供选项。

第26章 shell 工具

  • 创建以日期命名的文件及临时文件。
  • 信号。
  • trap命令以及如何捕获信号。
  • eval命令。
  • logger命令。

26.1 创建保存信息的文件

任何脚本都应该能够创建临时文件或日志文件。在运行脚本做备份时,最好是保存一个日志文件。这些日志文件通常在文件系统中保留几周,过时将被删除。 在开发脚本的时候,可能总要创建一些临时的文件。在正常运行脚本的时候,也要使用临时文件保存信息,以便作为另外一个进程的输入。可以使用 cat命令来显示一个临时文件的内容或把它打印出来。

26.1.1 使用date命令创建日志文件

在创建日志文件时,最好能够使它具有唯一性,可以按照日志文件创建的日期和时间来识别这些文件。我们可以使用date命令做到这一点。这样就能够使日期和时间成为日志文件名中的一部分。 为了改变日期和时间的显示格式,可以使用如下的命令:

date option + %format 

使用加号‘ +’可以设置当前日期和时间的显示格式。下面的例子将日期以日、月、年的 格式显示: 注意,如果希望在日期和时间的显示中包含空格,要使用双引号。 在文件名中含有日期的一个简单办法就是使用置换。把含有你所需要的日期格式的变量 附加在相应的日志文件名后面即可。 在下面的例子中我们创建了两个日志文件,一个使用了dd,mm,yy的日期格式,另一个使用了dd,hh,mm的时间格式。

26.1.2 创建唯一的临时文件

在本书的前面讨论特殊变量时,曾介绍变量 \(,该变量中保存有你所运行的当前进程的 进程号。可以使用它在我们运行的脚本中创建一个唯一的临时文件,因为该脚本在运行时的 进程号是唯一的。我们只要创建一个文件并在后面附加上\)即可。在脚本结束时,只需删除 带有\(扩展的临时文件即可。 Shell将会把\)解析为当前的进程号,并删除相应的文件,而不 会影响以其他进程号做后缀的文件。 在命令行中输入如下的命令: 这就是当前的进程号,如果你执行这个命令,看到的结果可能会有所不同。现在如果我 创建另一个登录进程并输入同样的命令,将会得到一个不同的进程号,因为我已经启动了一 个新的进程。 在执行rm /tmp/.$$时,shell实际上将该命令解析为 rm /tmp/.408。 记住,该进程号只在当前进程中唯一。例如,如果我再次运行上面的脚本,将会得到一个新的进程号,因为我已经创建了一个新的进程。 如果文件有特殊用途的话,那么创建含有日期的文件,就可以使你很容易地查找到它们。 而且还可以很容易地按照日期删除文件,因为这样一眼就能看出哪个文件是最新的,哪个文件是最“旧”的。 还可以使用这种方法来快速地创建临时文件,它们在当前进程中是唯一的。在脚本结束之前,也很容易删除这些临时文件。

26.2 信号

信号就是系统向脚本或命令发出的消息,告知它们某个事件的发生。这些事件通常是内存错误 ,访问权限问题或某个用户试图停止你的进程。信号实际上是一些数字。下表列出了最常用的信号及它们的含义。

信 号 信 号 名 含 义
1 SIGHUP 挂起或父进程被杀死
2 SIGINT 来自键盘的中断信号,通常是
3 SIGQUIT 从键盘退出
9 SIGKILL 无条件终止
11 SIGSEGV 段(内存)冲突
15 SIGTERM 软件终止(缺省杀进程信号)

还有信号 0,我们前面在创建 .logout文件时已经遇到过。该信号为“退出 shell”信号。为 了发出信号 0,只要从命令行键入exit,或在一个进程或命令行中使用 即可。 发送信号可以使用如下的格式:

kill [-signal no:| signal name] process ID

使用kill命令时不带任何信号或名字意味着使用缺省的信号15。 可以使用如下的命令列出所有的信号:

26.2.1 杀死一个进程

发送信号1将使一个进程重新读入配置文件。例如,你在运行域名服务(DNS)守护进程named,现在你对域名数据库文件做了某些修改,这时不需要杀死该守护进程再重新启动,只需使用kill -1命令向其发送信号 1。Named进程将重新读入它的配置文件。 下面的例子向系统中一个名为mon_web的进程发送信号 9(无条件终止)来杀死它。首先 使用ps命令得到相应的进程号。 果系统不支持ps-ef命令,那么可以使用ps xa。为了杀死该进程,我可以使用下面的两 种方法之一:

kill -9 157 

kill -s SIGKILL 157 

在有些系统中,不必使用 -s,例如: kill SIGKILL 157。 下面的脚本将根据进程名来杀死一个进程,拟被杀死的进程名作为该脚本的一个参数。 在执行相应的命令之前,将会首先检查是否存在这样的进程。在这里使用grep命令来匹配相 应的进程名。如果匹配成功,则向用户提示进程已经找到,并询问用户是否杀死该进程。最后使用kill -9命令杀死相应的进程。

26.2.2 检测信号

有些信号可以被应用程序或脚本捕获,并依据该信号采取相应的行动。另外一些信号不 能被捕获。例如,如果一个命令收到了信号 9,就无法再捕捉其他信号。 在编写shell脚本时,只需关心信号1、2、3和15。当脚本捕捉到一个信号后,它可能会采 取下面三种操作之一:

  • 1) 不采取任何行动,由系统来进行处理。
  • 2) 捕获该信号,但忽略它。
  • 3) 捕获该信号,并采取相应的行动。

大多数的脚本都使用第一种处理方法,这也是到目前为止本书中所有脚本所采取的处理 方法。 如果想要采取另外两种处理方法,必须使用 trap命令。

26.3 trap

trap可以使你在脚本中捕捉信号。该命令的一般形式为:

trap name signal(s) 

其中,name是捕捉到信号以后所采取的一系列操作。实际生活中, name一般是一个专门 用来处理所捕捉信号的函数。 Name需要用双引号(“ ”)引起来。Signal就是待捕捉的信号。 脚本在捕捉到一个信号以后,通常会采取某些行动。最常见的行动包括: 1) 清除临时文件。 2) 忽略该信号。 3) 询问用户是否终止该脚本的运行。下表列出了一些最常见的 trap命令用法: trap “” 2 3 忽略信号2和信号3,用户不能终止该脚本trap”commands” 2 3如果捕捉到信号 2或3,就执行相应的commands命令 trap 2 3复位信号2和3,用户可以终止该脚本 也可以使用单引号(‘’)来代替双引号(“”);其结果是一样的。

26.3.1 捕获信号并采取相应的行动

下面的例子一经运行就开始计数直至用户按 (信号2)。这时该脚本将会显示出当 前的循环数字,然后退出。 在本例中 trap命令的格式为:

trap "do_something" signal no:(s) 

26.3.2 捕获信号并采取行动的另一个例子

下面就是一个捕获信号后清除临时文件的例子。 下面的脚本在运行时不断使用df和ps命令向临时文件 HOLD1.\(和HOLD2.\)中写入相应的信息。你应该还记得 $$表示当前的进程号。当用户按时,这些临时文件将被清除。 当收到信号2或3时,尽管一般情况下这都不是误操作,但是为了安全起见,不妨给用户 一个选择的机会,这样用户在不小心按下 后,仍然可以撤消刚才的动作。 在下面的例子中,在脚本捕捉到信号2后将会向用户提供一个选择,询问用户是否真的要 退出。这里使用case语句来决定采取何种操作。 如果用户希望退出,他或她可以选择 1,此时当前函数会以状态 1退出,而另一个清除进程将会据此启动。如果用户并不希望退出,那么可以选择 2或不做任何选择,此时case语句将会使用户退回到脚本中原来的地方。在case语句中一定要包含用户输入空字符串的情况。 下面的函数在收到信号后,将会向用户提供选择:

26.3.3 锁住终端

下面的脚本是另一个捕获信号的例子。该脚本名为lockit,它将使用一个连续不断的while循环锁住终端。在该脚本中, trap命令捕捉信号 2、3和15。如果一个用户试图中断该脚本的运 行,将会得到一个不成功的提示。 在脚本初次执行时,将会被提示输入一个口令。在解锁终端时没有任何提示,可以直接输入口令并按回车键。该脚本会从终端读入所输入的口令,并与预先设置的口令做比较,如果一致就解锁终端。如果忘记了自己的口令,那么只好登录到另一个终端上并杀死该进程。在本例中没有对口令的长度加以限制—这完全取决于你。 如果你从另外一个终端上杀死了该进程,当再次回到这个终端时,可能会遇到终端设置问题,例如回车键不起作用。这时可以试着使用下面的命令,这样可以解决大部分问题。

$ stty sane 

26.3.4 忽略信号

在用户登录时,系统将会执行/etc/profile文件,根用户不希望其他普通用户打断这一进程。 他通常通过设置trap来屏蔽信号 1、2、3和15,然后在用户读当天的消息时重新打开这些信号。 最后仍然回到屏蔽这些信号的状态。 在编写脚本时也可以采用类似的办法。在脚本运行的某些关键时刻,比如打开了很多文件时,不希望该脚本被中断,以免破坏这些文件。通过设置trap来屏蔽某些信号就可以解决这个问题。在这些关键性的处理过程结束后,再重新打开信号。 忽略信号的一般格式为(信号 9除外):

trap""signal no:(s) 

注意,在双引号之间没有任何字符,为了重新回到捕捉信号的状态,可以使用如下的命 令:

trap"do something" signal no:(s) 

下面我们来总结一下上述方法。 trap ““1 2 3 15:忽略信号。 关键性的处理过程trap”my_exit” 1 2 3 15:重新回到捕捉信号的状态,在捕捉到信号后调用my_exit函数。 下面就是一个这样的例子,其中的“关键”过程实际上是一个 while循环,但它能够很好地说明这种方法。在第一个循环中,通过设置trap来屏蔽信号,但是在第二个例子中,又回到 捕捉信号的状态。 两个循环都只数到 6,不过在循环中使用了一个 sleep命令,这样就可以有充分的时间来实验中断该循环。

26.4 eval

eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量。 该命令对变量进行两次扫描。这些需要进行两次扫描的变量有时被称为复杂变量。不过我觉得这些变量本身并不复杂。 eval命令也可以用于回显简单变量,不一定是复杂变量。

26.4.1 执行含有字符串的命令

我们首先创建一个名为testf的小文件,在这个小文件中含有一些文本。接着,将cat testf赋给变量MYFILE,现在我们 echo该变量,看看是否能够执行上述命令。如果我们echo该变量,我们将无法列出testf文件中的内容。 让我们来试一下eval命令,记住eval命令将会对该变量进行两次扫瞄。使用 eval命令不但可以置换该变量,还能够执行相应的命令。第一次扫描进行了变量置换,第二次扫描执行了该字符串中所包含的命令 cat testf。 eval命令还可以用来显示出传递给脚本的最后一个参数。

26.4.2 给每个值一个变量名

可以给一个值一个变量名使用 eval命令 eval命令并不是一个在脚本中很常见的命令,但是如果需要对变量进行两次扫瞄的话,就要使用eval命令了。

26.5 logger命令

系统中含有相当多的日志文件。其中的一个日志文件叫作 messages,它通常位于 /var/adm 或/var/log目录下。一个名为 syslog的配置文件可以用来定义记录在messages文件中的消息,这些消息有一定的格式。如果想知道系统中的相应配置,可以查看/etc/syslog.conf文件。该文件中包含了用于发送各种不同类型消息的工具及它们的优先级。 这里我们并不想深入探讨UNIX和LINUX是如何向该文件中记录信息的。我们现在只要知道这些消息有不同的级别,从信息性的消息到关键性的消息。 还可以使用 logger命令向该文件发送消息。在使用该命令之前,最好查阅连机手册,因为在不同供应商所提供的操作系统上该命令的语法也有所不同。 不过,由于这里只涉及到信息性的消息,因此不必担心下面的命令不安全。你可能会出于下列的原因向该文件中发送消息:

  • 在某一个特定的时间段出现的访问或登录。
  • 你的某些执行关键任务的脚本运行失败。
  • 监控脚本的报告。

logger命令的一般形式为:

logger -p -I message 

其中:

-p:为优先级,这里只涉及到提示用户注意的优先级,这也是缺省值。
-i:在每个消息中记录发送消息的进程号。

26.5.1 使用logger命令

26.5.2 在脚本中使用logger命令

向日志文件中发送信息的一个更为合理的用途就是用于脚本非正常退出时。如果希望向 日志文件中发送消息,只要在捕获信号的退出函数中包含 logger命令即可。 除了使用 logger命令对一些关键性的脚本处理过程做日志外,我还用它来记录使用调制解 调器连接系统的用户。下面的一段脚本记录了从串口 tty0和tty02连接到系统中的用户。 当希望在系统全局的日志文件中记录信息的时候, logger命令是一个非常好的工具。

第27章 几个脚本例子

例子:

  • pingall:一个按照/etc/hosts文件中的条目逐一ping所有主机的脚本。
  • backup_gen:一个通用的备份脚本,能够加载缺省设置。
  • del.lines:一个引用sed命令的脚本,能从文件中删除若干行。
  • access_deny:一个能够阻止某些特定用户登录的工具。
  • logroll:一个能够清除超过某一长度的日志的工具。
  • nfsdown:一个快速 unmount所有nfs文件系统的工具。

27.1 pingall

pingall的脚本在夜间运行,把它作为常规报告脚本的一部分。它 能够按照 /etc/hosts文件中的条目逐一ping所有的主机。 该脚本列出 /etc/hosts文件并查找其中的非注释行(不以 #开头的行)。然后使用一个while循环读入所有的行,接下来使用awk分析出每行的第一个域,并把它赋给变量ADDR。最后使用for循环逐一ping相应的地址。

27.2 backup_gen

这个脚本并不是因为它展示了如何备份目录,而是因为它是一个同其他脚本共享设置的很好例子。 backup_gen是一个用于备份的脚本,它从一个缺省的配置文件中读入设置,然后根据这些参数对系统进行备份。用户可以根据自己的需要改变这些缺省设置。 这是一个不同脚本如何使用相同设置或仅在自己运行期间改变相应设置的极好例子。当该脚本执行时,它首先确认源文件backup.defaults是否存在,如果不存在,则退出。 该脚本在运行时,会显示出一个题头和缺省设置,并询问用户是否需要改变任何缺省设置。如果用户回答“是”,在他们修改设置之前,该脚本就会提示他们输入一个代码,用户可以有三次机会;如果输入正确的代码后仍无法改变设置,这就意味着用户必须要使用缺省设 置。一般来说,在输入正确代码后,用户可以改变下列设置([ ]中的为缺省设置):

  • 磁带设备[rmt0]可以选择rmt1和rmt3
  • 备份完成后是否向系统管理员发邮件[是]可以选择否
  • 备份的类型[全备份]可以选择普通备份或sybase备份 脚本中使用了一些临时变量来保存被修改的设置。

用户可以按回车键选择缺省设置。下列设置不能被改变: 备份日志文件名。用户代码。 接着所有的改变会生效。在这些改变生效之后,相应的临时变量又会被重新赋予缺省值。 在备份进行之前,首先要测试磁带设备。备份过程使用find和cpio命令,它们从设置文件中读入相应变量的缺省值,或使用用户设定的值。

27.3 del.lines

这个脚本只是包装了一下sed命令,但它能够使用户很方便地使用,他们非常喜欢用。脚本一般都不长。如果你认为写一个脚本能够使某些任务自动化,能够节约时间,那么你就可以编写一个脚本。这个脚本可以处理一个或多个文件。每个文件在用sed删除空行之前要先核实是否存在。 sed的输出被导入一个文件名中含有$$的临时文件,最后这个临时文件又被移回到原来的文件中。 该脚本使用shift命令取得所有的文件名,用while循环逐个处理所有的文件,直至处理完为止。 可以使用del.lines-help获得一个简短的帮助。你也可以创建一个更好的帮助。

27.4 access.deny###

在对系统进行某些更新时,你可能不希望用户登录,这时可以使用/etc/nologin文件,大多数系统都提供这个文件。

一旦在 /etc目录中使用 touch命令创建了一个名为nologin的文件, 除root以外的任何用户都将无法登录。

如果系统不支持这种方法,你一样还可以做到这点—可以自己创建这个文件,下面就是具体的做法。

现在,可以通过在/etc目录下创建 nologin文件来阻止除根用户以外的其他用户登录。记住,该文件要对所有用户可读。

当决定恢复用户登录时,只要删除该文件即可。rm /etc/nologin 上述办法可以很方便地组织除根用户外的所有用户登录。

如果希望临时禁止某个用户登录,可以修改/etc/passwd文件,把该用户的口令域的第一个字符变成*。不过,这个问题比较复杂,在操作之前一定要搞清楚,否则会带来系统性的问题。

LINUX提供了一个工具,可以通过它在login.access文件中写入用户名和用户组。该文件 可以用来允许或禁止用户对系统的访问。 这里有一个上述工具的简化版本deny.access。该脚本从/etc/profile文件中运行,它读入一个名为lockout.users的文件。该文件包含有禁止登录的用户名。如果该文件中出现了all这个单词,那么除root以外的所有用户都将被禁止登录。 下面解释该脚本的工作过程。 首先,通过设置trap忽略所有的信号,这样用户就无法中断它的执行。如果文件lockout.users存在,那么脚本将会继续运行。 它首先检查该文件中是否存在单词all,如果存在,就不再检查该文件中的其他用户名,并禁止除根用户以外的所有其他用户登录。不要使用注释来屏蔽单词all,因为这样它仍然有可能起作用。不过你可以注释用户名。 如果单词all被找到,那么除root外的所有用户都将无法登录。为了准确起见,在该脚本中 使用了grep的精确匹配模式all>。这时用户将会在屏幕上看到系统不可用的消息。 该脚本中的主要函数是get_users。它读入文件lockout.users,忽略所有以#开头的注释行。 它通过比较用户名来确保用户名root没有出现在该文件中,即使出现也不会禁止 root登录,否 则后果将难以想象。 当前正在登录的用户名可以从变量LOGNAME中得到,并与变量 NAMES做比较,而变量NAMES的内容来自于lockout.users文件。如果匹配,相应用户的登录进程将被终止。 我在几个拥有近 40个用户的系统上运行该脚本,它并没有影响用户登录的速度。当用户外出超过一周或者用户午餐,而我需要对系统进行更新时,我就使用该脚本临时锁住相应的帐户。 需要在/etc/profile文件中加入这样一行。我把它加在该文件的末尾,这样即使用户无法登 录,也可以在此之前看见当前发给他的新消息。 . /apps/bin/deny.access /apps/bin目录是我存放全局性脚本的地方—你可能把这些脚本放在另外的目录中,不过一定要确保所有用户都对该脚本及存放它的目录具有执行权限。 如果得到“权限不足”的错误提示,那说明该脚本或目录的权限不足。我的lockout.users文件放在/apps/etc目录下。如果你的系统的目录结构有所不同的话,应该作出相应的调整。由于该文件在登录时被引用,可以使用set命令看到相应的函数(不过无法看到 lockout.users文件)。如果你觉得这不妥,只要在这些函数执行后使用unset命令去掉它们即可。可以把unset命令直接放在 /etc/profile文件中该命令行之后

27.5 logroll

系统中的有些日志文件增长十分迅速,每天手工检查这些日志文件的长度并倒换这些日志文件通常是给文件名加个时间戳是非常乏味的。

于是我决定编写一个脚本来自动 完成这项工作。该脚本将提交给cron进程来运行,如果某个日志文件超过了特定的长度,那么它的内容将被倒换到另一个文件中,并清除原有文件中的内容。 你可以很容易地改编这个脚本用于清除其他的日志文件。

我使用另外一个脚本来清除我的 系统日志文件,它每周运行一次,截断相应的日志文件。如果我需要再回头看这些日志文件,只需在备份中寻找即可,这些日志文件的备份周期为 16周,这个周期长度应该说是足够了。 该脚本中日志文件的长度限制是由变量 BLOCK_LIMIT设定的。这一数字代表了块数目,在本例中是8块每块大小为4K字节)。

可以按照自己的需求把这一数字设得更高。所有我要 检查的日志文件名都保存在变量LOGS中。这里使用了一个for循环来依次检查每一个日志文件,使用du命令来获取日志文件长度。如果相应的文件长度大于 BLOCK_LIMIT变量所规定的值,那么该文件将被拷贝到一个文件 名含有时间戳的文件中,并改变这个文件所属的组,原先的文件长度将被截断为0。 该脚本由cron每周运行几次,生成了一些文件名中含有时间戳的日志文件备份,这样如果系统出现了任何问题,我还可以回到这些备份中查找。

27.6 nfsdown

如果系统中包含nfs文件系统,你将发现下面的脚本非常实用。我管理着几台主机,不时地需要在工作时间重启动其中的某台机器。这种重启动过程当然是越快越好。 由于我在好几个机器上都挂接了远程目录,我不想依靠系统的重启动过程来卸载这些nfs 文件系统,宁愿自己来完成这个工作。这样还可以更快一些。 只要运行这个脚本就可以迅速卸载所有的nfs文件系统,这样就能更快的重新启动机器。该脚本的LIST变量中含有提供nfs目录的主机名使用 for循环逐一卸载相应的目录,用grep命令在df命令的结果中查找 nfs文件系统。 nfs目录的mount形式为: machine:remote_directory这一字符串被保存在变量 NFS_MACHINE中。在umount命令中使用了该变量。

第28章 运行级别脚本

如果希望在系统启动时自动运行某些应用程序、服务或脚本,或者在系统重启动时能够正确地关闭这些程序,那么需要创建运行级别脚本。

  • 运行级别。
  • 如何创建 rc.scripts。
  • 如何在不同的运行级别实现相应的 rc.scripts。
  • 如何从inittab中启动应用程序。

能够创建运行级别脚本就意味着能够更灵活地控制系统。如果需要在某一个特定的运行级别启动或停止程序,就得创建运行级别脚本(它们通常被称为rc.script)。 任何用关键字start或stop调用的、能够启动或停止程序运行的脚本都可以看作是一个rc.script。

注意,应当由用户来保证他或她所提交的脚本是一个有效的脚本,能够正确地启动或停止某一服务。 运行级别配置目录的机制使得rc.script只在系统切换运行级别时有效。它不负责检查某一运行级别中所有的特定服务是否都已经被启动或停止。这是shell编程者的事。

28.1 怎么知道系统中是否含有运行级别目录

rc.scripts 一般保存在(实际上是个链接,这一点我们将在后面讲述)/etc/rcN.d或/etc/rc.d/rcN.d目录下, 其中,N是一个数字。通常是7个,因为rcN.d目录的序号是从 0到6,不过在系统上可能会 有另外几个目录,如 rcS.d。这并不重要,这里我们只关心带有数字的目录。 如果我们使用 cd命令进入这些rcN.d目录,会发现这些目录中的 rc.scripts实际上是一些链接。

28.2 确定当前的运行级别

作为shell编程者,应当了解rc.scripts是什么,它们是被 怎样放置到运行级别配置目录中的。顺便说一下,如果想知道当前的运行级别,可以用下面 的命令: $ who -r 在‘run level’后面的数字就是当前的运行级别。后面的时间是系统最近一次重启动的时间。

28.3 快速熟悉inittab

运行级别目录中含有一系列启动服务的脚本。这里的“服务”可以是守护进程、应用程序、服务器、子系统或脚本进程。 在系统启动的过程中,将会启动一个名为init的进程(它是系统中所有进程的祖先)。

它所要完成的一部分工作就是看看需要启动哪些服务,应当缺省地进入哪一个运行级别。它通过查看一个名为inittab的配置文件来获得上述信息,该配置文件位于/etc目录下。init进程还按照该文件中的设置加载特定的进程。

如果需要编辑这个配置文件,一定要先做一个备份。如果该文件被破坏或出现“降级”错误,系统将无法正常启动,到那 时,将不得不进入单用户模式并修正该文件。

inittab文件所包含的域具有严格的格式。该文件中每个条目的格式为: id:rstart:action:process其中,id域是相应进程的唯一标识。 rstart域所包含的数字表示运行该进程的级别。 action域告诉 init进程如何对待 process所对应的进程。

这里可以有很多种动作,但是最常见的是wait和respawn。wait意味着当进程启动后等待它结束。 respawn则意味着如果该进程不 存在,则启动相应的进程,如果它存在,那么只要它一掉下来就立即重新启动它。

28.4 运行级别

init进程在系统完全就绪之前所做的最后几项工作之一就是执行缺省运行级别所包含的所有脚本。该进程是通过 /etc/rc.d/rc或/etc/rc.init来启动这些脚本的。它的作用是首先杀死该运 行级别所包含的进程再启动这些进程。 但是它怎么知道该启动或停止哪些服务呢? rc或rc.init脚本将会使用for循环来依次查看相应运行级别目录中的文件,给每一个链接名以K开头的相应脚本赋予参数stop; 给每一个链接名以S开头的相应脚本赋予参数start。 在运行级别切换时,上述脚本也会完成同样的工作,只不过根据相应的运行级别来启动或停止对应的脚本。 rcN.d目录中的脚本只是一些链接—真正的脚本保存在其他的目录中。它们通常都放置在/usr/sbin/init.d或/etc/init.d目录中。在这个目录中含有一些能够启动或停止某一服务的脚本。这些脚本的名字最好能够表示 出它所实现的功能,形如 rc.<功能>,其中rc表示运行命令( run command )或运行控制( run control),或者就像某些系统管理员所称的那样“真正关键的”(‘real crucial’)。 一般来说,rc.scripts都应当能够接受这样的参数: rc.name stop:停止该服务。 rc.name start:启动该服务。 可选的参数包括 restart和status。其他任何参数都应当给出相应的用法说明。注意,可以手工运行这些脚本。 现在我们已经知道运行级别脚本应当具有什么样的功能,下一步就是把它们放置在相应的rcN.d目录中。不过在此之前我们先来了解一下系统运行级别。

28.4.1 各种运行级别

系统含有七种运行级别 (见表28-1)。不同的系统在某些运行级别上稍有差别。 在将一个脚本放置在不同的运行级别目录中之前,首先应当弄清楚打算在哪一个运行级 别启动或停止相应的服务?一旦弄清楚这一点,就可以接着进行下面的步骤了。 表28-1 各个运行级别的用途

运行级别 0	启动和停止整个系统
运行级别 1	单用户或管理模式
运行级别 2	多用户模式;部分网络服务被启动。有些系统将其作为正常运行模式,而不是级别 3
运行级别 3	正常操作运行模式,启动所有的网络服务
运行级别 4	用户定义的模式,可以使用该级别来定制所需要运行的服务
运行级别 5	有些UNIX操作系统变体将其作为缺省 X-windows模式,还有些系统把它作为系统维护模式 运行级别 6	重启动

28.4.2 运行级别脚本的格式

rcN.d目录中的脚本都是一些链接,这样是为了省去不必要的副本。这些链接的格式为:

Snnn.script_name
或
Knnn.script_name

其中,S:代表启动相应的进程 K:代表杀死相应的进程 nn:是00至99的两位数字,不过在有些系统中是 000至999三位数字。在不同目录中的链 接应采用同一数字。例如,如果某个服务在 rc3.d中启动时名为 S45.myscript,那么如果希望它 在rc2.d中启动,应当使用链接名 S45.myscript。 script_name:相应脚本的文件名,根据所在操作系统的不同,它们可能位于下列目录中:

/usr/sbin/init.d
/etc/rc.d
/etc/init.d

当init进程调用相应的运行级别脚本时,杀进程按照从高到低的 K序号进行,即K23,myscript K12.named;而启动进程按照从低到高的序号进行。如果使用的是 LINUX系统,K序号将按照从高到低的顺序执行。

28.4.3 安装运行级别脚本

如果想要安装自己的运行级别脚本,必须:

  • 编写该脚本,确保它符合调用标准。
  • 确信它能够启动或终止相应的服务。
  • 将该脚本放置于(取决于操作系统)/etc/init.d或/usr/sbin/init.d或/etc/rc.d中。
  • 在相应的 rcN.d目录中按照合理的命名方式创建链接。

下面的脚本能够启动或停止一个名为rc.audit的审核应用程序。

该服务运行于级别 3、5、4,停止于级别 6、2、1。通过查看rcN.d中的条目,我们发现序号 35空闲,于是就使用该序号。

实际上,系统并不对使用已占用的序号作任何检查。start选项将使该审核进程启动相应的审核系统,而stop选项将使它终止该系统的运行。当然,在将自己的运行级别脚本放置在init.d目录中之前,应该首先对该脚本进行测试让我们假定该脚本已经通过了测试。它能够正确地启动和停止审核服务。现在我们把该 脚本放置在相应的运行级别目录中。

在本系统中,rcN.d目录位于/etc/rc.d目录下,而我的运行级别脚本保存在/etc/rc.d/init.d目录下。如果系统目录结构与上面的不同,那么需要对下面的命令作相应的调整。我们首先启动该脚本—记住启动脚本所使用的链接名是以S打头的。 我们已经创建了相应的链接。ls-l命令的结果显示该链接指向 /etc/init.d/rc.audit文件。我本应该在链接命令中给出全路径,不过没有这个必要。现在我只要进入其他的相关目录(rc4.d和rc5.d)使用同样的命令就可以启动其他相应的服务。

在其他相关目录中,也可以如法炮制,停止相应的审核服务。现在当系统重启动时(运行级别6),它将被停止;在运行级别切换到 2或1时也是如此。该服务在运行级别4或5中同样也会被启动。

28.5 使用inittab来启动应用程序

我们还可以用其他的方法来启动应用程序。可以通过在 inittab文件中加入相应的条目来做到这一点。在我所管理的有些系统上,我就使用了这种方法,这倒不是因为这些系统中没有 运行级别目录,而是由于我有几个用于系统检查的脚本需要在系统刚刚就绪之后运行。使用 inittab是实现上述功能的理想途径。 这里我们给出一个例子,我打算在系统运行在级别3时运行我的一个磁盘镜像检查脚本。 首先我确定该脚本能够正确运行,然后对 inittab文件做备份。

$ cp /etc/initab /etc/inittab.bak

接下来编辑 inittab文件,在该文件末尾加入这样一个条目:

保存并退出。上面的一条意思是: 行首的rc.diskchecker是该进程在运行级别3中的唯一标识。该进程只运行一次。所要运行的脚本是/usr/local/etc/rc.diskchecker,所有的输出都被送到控制台。

28.6 启动和停止服务的其他方法

如果不想把 /etc/inittab文件弄得过于杂乱,还有其他的方法可以实现启动和停止服务的功 能。大多数系统都含有一个名为 rc.local的文件,一般来说也是位于 /etc目录下。该脚本文件将 在inittab和运行级别脚本之后运行。可以在该文件中加入任何命令,或从中调用最习惯用的启 动脚本。 有些系统还在 /bin目录下 (更多的是在 /usr/sbin目录下 )含有一个名为 shutdown的脚本文件。 可以使用它来关闭某些服务。

第29章 cgi脚本

  • 基本cgi脚本。
  • 使用服务器端内嵌 (Server Side Includes,SSI)。
  • get 方法。
  • post 方法。
  • 创建交互式脚本。
  • 能够自动重载 Web页面的cgi脚本。

运行Web服务器并不一定需要有网络环境,可以在本地主机上运行它。这里,我们假定你已经安装了Web服务器 (apache、Cern等等)以及浏览器(Netscape、Internet Explorer 等等)。 另外,该服务器应当允许运行 cgi脚本。一般来说缺省值是禁止运行cgi脚本的,要运行,只要 将配置文件中相应的一行注释掉即可。后面我们会更详细地讨论这一问题。

29.1 什么是Web页面?

Web页面或文挡是包含有HTML标记的文件。

当浏览器连接到一个 Web页面上时,浏览器就会根据相应的 HTML标记来显示该页面。

Web页面中可以含有非常丰富的信息,它可以包含 指向其他页面的链接、各种色彩、高亮标题、各种字体、直线、表格,还可以包含图像和声音。

Web页面可以分为两类:动态的页面和静态的页面。静态的页面是用于显示信息或下载 文件。而动态的页面是交互型的,它们可以按照你所提供的信息产生相应的结果。动态页面 还可以用于显示实时变化的信息,如股票价格,或用于完成某些监视任务。如果想要执行这 种类型的处理,就需要编写脚本。 如果一个Web服务器能够交换信息脚本,那么它必须支持一种被称为公共网关接口的协议,即大家所熟悉的cgi。

29.2 cgi

cgi是一种规范,它规定了获取信息的脚本如何从服务器中取得信息或向服务器中写入信息。这种脚本或cgi脚本可以用任何语言来实现。最为流行的是Perl语言,不过你将会发现, 也可以用普通的shell脚本来实现

29.3 连接Web服务器

可以使用统一资源定位符(URL)连接Web服务器。 URL包含两部分信息:

  • 协议。
  • 地址和数据。

其中,协议包括 http、ftp、mailto、file、telnet和news。这里我们只关心 http协议(超文本 传输协议 )。 地址一般是 DNS域名或服务器主机名,也可以是 IP地址。其他数据可以是你所要访问文件的实际路径名。 所有的连接都基于 TCP协议之上,缺省的端口号为80。 如果Web服务器在你的本地主机上,而相应的主页为 index.html,那么可以使用下面的 URL:http://localhost/index.html 一般来说,index.html是缺省下载的文件,即该页面是你的 Web服务器的缺省页。这样,你可以只输入如下的 URL:http://localhost/

29.4 cgi和HTM脚本

当浏览器发出下载页面的请求时,Web服务器将会对收到的 RL进行分析。

如果其中含有cgi-bin,服务器将打开一个连接,通常是连接相应cgi脚本的管道。该cgi脚本所有的输入输出都将通过该管道。 如果该cgi脚本用于显示 Web页面,那么它的输出中必须要包含必要的HTML标记,这样该页面才能够按照服务器所能够理解的格式被显示出来,因此我们有必要了解一些 HTML的知识。 Web服务器将该页面返回给发出请求的浏览器显示出来。 表29-1列出了一些常用的HTML标记。

29.4.1 基本cgi脚本

所有的cgi脚本都应当位于Web服务器的cgi-bin目录中,不过在不同的服务器中该目录会有所不同。

可以通过查看配置文件srm.conf中ScriptAlias一段来改变该目录的位置,并允许该 服务器运行cgi脚本。

所有的脚本文件名都应以 .cgi 做后缀。而其他Web页面都位于html或htdocs目录下,并且带有.html后缀。所有的脚本都应具有这样的权限。

chmod 755 script.cgi所有Web页面连接的缺省用户身份为nobody,不过可以通过配置httpd.conf文件来改变这一设置。尽管我曾经说过本章并不是关于 Web服务器配置的,不过正好可以借这个机会检查一下passwd文件,看看nobody用户的登录是否被禁止。如果想防止任何用户以 nobody的身份 从某一个物理端口上登录,只要在/etc/passwd文件中该用户口令域的第一个字符前面加入一 个星号*即可。

表29-1 基本HTML标记

<HTML></HTML>	|HTML文挡的开头和结束|
<HEAD></HEAD>|	标题部分的开头和结束|
<TITLE></TITLE>	主题的开头和结束
<BODY></BODY>	页面部分的开头和结束
<Hn></Hn>	不同层次的标题, 1代表最大的字体
<P></P>	段落的开头和结束
<BR>	换行
<HR>	水平线
<PRE></PRE>	预定义格式文本的开头和结束,其中的所有跳格、行都保持原样。
<B></B>	黑体
<I></I>	斜体
<OL></OL>	有序号的列表
<A HREF=url>link</A>	超文本链接
<FORM></FORM>	表单
METHOD	post或get方法
ACTION	地址
<INPUT...>	数据输入
NAME	变量名
SIZE	以字符计的文本框的宽度
TYPE	复选框、单选框、复位、提交
<SELECT...>	下拉式菜单
NAME	变量名
SIZE	要显示的列表的项数
<OPTION VALUE>	将用户选择的值返回给 NAME变量
</SELECT>	结束列表框

如果脚本不能正常工作,应当首先查看错误日志,因为所有的错误都记录在这些日志中。 如果使用apache作为Web服务器,那么相应的日志文件位于 /etc/httpd/logs或/usr/local/apache/logs 目录下,这取决于 Web服务器的安装路径。 cgi脚本可以在命令行方式下运行测试,当然这时只能看到文本形式的输出,但是这种输出结果有助于调试该脚本。

29.4.2 显示shell命令输出

现在我们在脚本中加上一条shell命令,这样就可以在浏览器中显示该命令的输出。我们将显示当前登录的用户数,这通过将 who命令的输出经管道传递给wc命令就可以实 现。还将显示当前的日期。

29.4.3 使用SSI

使用cgi脚本产生一个Web页面并显示少量信息有点杀鸡用牛刀。 例如我们用cgi脚本产生了一个页面而仅仅是为了显示当前日期。 如果我们能够把cgi脚本嵌入到HTML文挡中,让该脚本的输出显示在普通的页面中岂不更好?这样做是可行的,下面我们就将讨论这一内容。

为了内嵌cgi脚本,我们必须使能服务器端内嵌(SSI)。这样,在显示一个页面时,它将会把SSI命令替换为相应命令或脚本的输出。一些含有服务器和命令信息的特殊环境变量也是全局可见的。 为了让服务器能够知道替换文挡中的相应SSI命令,需要使能 SSI,应当去掉配置文件中含有server-passed的一行开头的注释符,在 apache上是这样的:需要重新启动Web服务器软件,使用kill-1命令使该服务器重新读入配置文件。含有SSI的页面应当具有后缀.shtml,而不是.html。

29.4.4 访问计数器

现在让我们来创建一个能够显示访问次数的页面。你肯定见过类似的页面:“你是第n个访问本站点的用户”。

我们还将显示该页面的最新更改时间。不要忘记将下面的脚本放置在cgi-bin目录中,起名为hitcount.cgi。

如你所见的,该脚本读入../cdi-bin/counter文件中的值并将其赋给变量access,将该变量加1,显示该变量,最后将新的值写回到 ../cgi-bin/counter文件中。

现在我们创建一个名为counter的文件。我们只需在其中放置一个初始值,这里我们取1。于是,操作步骤为:创建一个名为 counter的文件并写入1(不要加引号),然后保存并退出。

由于每个用户都需要使用该文件,文件属主、同组用户及其他用户都应对其具有读和写的权限。

$ chmod 666 counter 

现在还需要在Web根目录下(该目录通常是放置所有其他HTML文挡的地方,一般是htdocs或html目录)创建一个相应的.shtml文件。

下面就是该文件,千万不要忘记带.shtml:在使用 SSI时LAST_MODIFIED变量及其他变量是全局可见的。

可以通过访问apache的Web站点(www.apache.org)得到所有在使用SSI时全局可见的特殊变量的详细描述。

我们来看下列的 SSI命令: 在本例中, cgi脚本hitcount是这样运行的:命令是exec。参数为cgi。其中的value是要运行的脚本。我已经改变了配置文件,把这个页面作为主页,而不是index.html。不过你仍然可以通过 在URL中使用全路径来访问 index.html页面。

如果想改变缺省页,可以编辑 srm.conf文件。该文件中含有这样一行:

DirectoryIndex你会看到文件名index.html显示在该条目中。

把它改成所希望的缺省页即可。不要忘记关闭并重新启动Web服务器以使该改动生效。要访问该脚本,可以在浏览器的URL框中输入:http:///main.shtml或http://(如果该页面是缺省页的话)。

图29-4显示了刚才创建的页面;可以刷新该页面观察计数器的增加。注意,LAST_MODIFIED变量的值也被显示出来。

可以通过在cron中加入一行,每天夜里向counter文件中写入1来复位该计数器的值。

29.4.5 使用一个链接来显示当前Web环境变量

你在自己机器上所看到的可能与此有所不同。在运行不同的脚本时,一些相应的环境变量值将会随之改变。

29.4.6 其他常用的环境变量

表29-2列出了最常用的 cgi环境变量。其中有些变量可以用 env或set命令显示出来。

表29-2 常用的cgiWeb服务器变量

DOCUMENT ROOT	Web服务器的主目录,是放置 HTML文挡的地方
GATEWAY_INTERFACE	cgi的版本
HTTP_ACCEPT	可接受的各种 MIME类型
HTTP_CONNECTION	缺省的 HTTP连接
HTTP_HOST	本地主机名
HTTP_USER_AGENT	客户端浏览器
REMOTE_HOST	远程主机
REMOTE_ADDR*	远程主机的 IP地址
REQUEST_METHOD	传递信息的方法
SCRIPT_FILENAME	cgi脚本的绝对路径
SCRIPT_NAME	cgi脚本的相对路径
SERVER_ADMIN	Web服务器管理员的邮件地址
SERVER_NAME	服务器的主机名、 DNS或IP地址
SERVER_PROTOCOL	连接所使用的协议
SERVER_SOFTWARE	Web服务器软件名
QUERY_STRING	get方法所传递的数据
CONTENT_TYPE	MIME类型
CONTENT_LENGTH	post方法所传递的字节数

29.5 get和post方法简介

到现在为止,我们只是向屏幕上输出。要想从用户那里得到信息,我们需要使用表单,这也是cgi为什么如此流行的原因。你需要有获得用户输入的能力。有了表单就可以显示文本框、下拉式列表框和单选框。在用户通过键入或选择向表单输入了一些信息之后,他可以点击发送按钮将这些信息发 送给某个脚本,在这里是 cgi脚本。我们需要使用 get或post方法来收集这样的信息。

29.5.1 get方法

任何表单的缺省操作都是get方法。get方法是从静态HTML页面获取文件的方法。当用户点击“提交”按钮时,用户选择的信息将以编码字符串的形式附加在服务器URL 的后面。服务器环境变量 QUERY_STRING保存了编码字符串。变量 REQUEST_METHOD保 存了该表单所使用的方法。

区域型文本框允许用户输入超过一行的文本,而不是像标准文本框那样只能输入一行(在本例中是4行,每行宽30个字符),所有的输入都将赋给变量textarea。

发送数据时需要使用 submit作为输入类型。对编码字符串解码当用户点击提交按钮时,相应的信息被赋给了变量 QUERY_STRING,这些信息是以下面 的格式编码的: 所有的空格用 +来替代。所有的值域用 &隔开。

所有的值和相应域用=隔开。所有的符号和一些特殊字符用% xy的形式表示,其中xy是该字符的 16进制ASCII码。看一 下QUERY_STRING变量就知道,在textarea变量中含有很多这样的字符。 cgi协议声明,所有采用%xy形式表示的特殊字符(其中xy为16进制数)都被转换为对应的 ASCII字符。这种 16进制字符包括特殊字符 &、%、 +、=、(、)及所有 ASCII码超过 127的其他 特殊字符。例如字符“(”应为%29。这种16进制字符产生于文本框,用户可能会在这些地方输入这样的字符。不过,它们也可以出现在下拉式列表框中。

为了解码相应字符串,我们应当: 将所有的&替换为换行。将所有的+替换为空格.将所有的=替换为空格.将所有的%xy替换对应的ASCII字符。在

完成上述转换之后,我们可能需要访问某个变量,这样就可以根据用户发送的信息来进行某些处理。解码只是所有工作的一部分,尽管这是最繁重的一部分。如果想访问这些变量,可以使用 eval命令。

使用表单时,get方法是缺省的方法。根据所处的环境,使用get方法有两个潜在的问题。在发送信息时,整个编码字符串都附加在服务器URL的后面,这样所发送的信息可以从URL框中看到。可能你会认为这没什么大不了的,如果你是通过网络发送公司或个人的信息,这就是一个值得注意的问题了。如果表单具有很多的输入域,那么QUERY_STRING变量将会变得很长。大多数人在使用cgi时都用post作为表单输入的方法。

29.5.2 post方法

post方法的字符串编码方式与get方法相同。它们所不同的是获取数据的方法, post方法是从标准输入读入的。如果想用post方法来发送数据,只要把表单操作语句中的get替换为post即可。

29.5.3 填充列表项

要想使页面成为真正的动态页面,可能需要动态地使用某个文件中的信息来填充列表项,而不是把它们编写进cgi脚本。

29.5.4 自动刷新页面

在使用cgi脚本实现监视或看门狗功能时,如果能够让页面不断自动刷新就方便多了。想要实现这样的功能,需要不断调用自己的脚本或页面。