Linux 启动一个项目

来源:互联网 发布:mac自带壁纸 编辑:程序博客网 时间:2024/06/12 22:42
这个项目的目的是为了了解怎样使用各种各样的shell 功能来创建程序,更重要的是,创建好程序。我们将要编写的程序是一个报告生成器。它会显示系统的各种统计数据和它的状态,并将产生HTML 格式的报告,所以我们能通过网络浏览器,比如说Firefox 来查看这个报告。通常,创建程序要经过一系列阶段,每个阶段会添加新的特性和功能。我们程序的第一个阶段将会产生一个非常小的HTML 网页。

第一阶段:最小文档

首先我们需要知道的事是一个规则的HTML 文档的格式。它看起来像这样:

<HTML>
            <HEAD>
                         <TITLE>Page Title</TITLE>
            </HEAD>
            <BODY>
                         Page body.
            </BODY>
</HTML>

如果我们将这些内容输入到文本编辑器中,并把文件保存为foo.html,然后我们就能在Firefox 中使用下面的URL 来查看文件内容:

file:///home/username/foo.html

程序的第一个阶段将这个HTML 文件输出到标准输出。我们可以编写一个程序,相当容易地完成这个任务。启动我们的文本编辑器,然后创建一个名为/bin/sys info page 的新文件:

[me@linuxbox ~]$ vim ~/bin/sys_info_page
随后输入下面的程序:
#!/bin/bash
# Program to output a system information page
echo "<HTML>"
echo "            <HEAD>"
echo "                         <TITLE>Page Title</TITLE>"
echo "            </HEAD>"
echo "            <BODY>"
echo "                          Page body."
echo "            </BODY>"
echo "</HTML>"

我们第一次尝试解决这个问题,程序包含了一个shebang,一条注释(总是一个好主意)和一系列的echo 命令,每个命令负责输出一行文本。保存文件之后,我们将让它成为可执行文件,再尝试运行它:

[me@linuxbox ~]$ chmod 755 ~/bin/sys_info_page
[me@linuxbox ~]$ sys_info_page

当程序运行的时候,我们应该看到HTML 文本在屏幕上显示出来,因为脚本中的echo 命令会输出发送到标准输出。我们再次运行这个程序,把程序的输出重定向到文件sys info page.html 中,从而我们可以通过网络浏览器来查看输出结果:

[me@linuxbox ~]$ sys_info_page > sys_info_page.html
[me@linuxbox ~]$ firefox sys_info_page.html

到目前为止,一切顺利。
在编写程序的时候,尽量做到简单明了,这总是一个好主意。当一个程序易于阅读和理解的时候,维护它也就更容易,更不用说,通过减少键入量,可以使程序更容易书写了。我们当前的程序版本工作正常,但是它可以更简单些。实际上,我们可以把所有的echo 命令结合成一个echo 命令,当然这样能更容易地添加更多的文本行到程序的输出中。那么,把我们的程序修改为:

#!/bin/bash
# Program to output a system information page
echo "<HTML>
                      <HEAD>
                                  <TITLE>Page Title</TITLE>
                      </HEAD>
                      <BODY>
                                   Page body.
                      </BODY>
</HTML>"

一个带引号的字符串可能包含换行符,因此可以包含多个文本行。Shell 会持续读取文本直到它遇到右引号。它在命令行中也是这样工作的:

[me@linuxbox ~]$ echo "<HTML>
>              <HEAD>
                            <TITLE>Page Title</TITLE>
>              </HEAD>
>              <BODY>
>                           Page body.
>              </BODY>
></HTML>"

开头的“>”字符是包含在PS2shell 变量中的shell 提示符。每当我们在shell 中键入多行语句的时候,这个提示符就会出现。现在这个功能有点儿晦涩,但随后,当我们介绍多行编程语句时,它会派上大用场。

第二阶段:添加一点数据

现在我们的程序能生成一个最小的文档,让我们给报告添加些数据吧。为此,将做以下修改:

#!/bin/bash
# Program to output a system information page
echo "<HTML>
         <HEAD>
                     <TITLE>System Information Report</TITLE>
         </HEAD>
         <BODY>
                     <H1>System Information Report</H1>
         </BODY>
</HTML>"

我们增加了一个网页标题,并且在报告正文部分加了一个标题。

变量和常量

然而,脚本存在一个问题。请注意字符串“System Information Report”是怎样被重复使用的?对于这个微小的脚本而言,它不是一个问题,但是让我们设想一下,我们的脚本非常冗长,并且我们有许多这个字符串的实例。如果我们想要更换一个标题,我们必须对脚本中的许多地方做修改,这会是很大的工作量。如果我们能整理一下脚本,让这个字符串只出现一次而不是多次,会怎样呢?这样会使今后的脚本维护工作更加轻松。可以这样做:

#!/bin/bash
# Program to output a system information page
title="System Information Report"
echo "<HTML>
            <HEAD>
                         <TITLE>$title</TITLE>
            </HEAD>
            <BODY>
                         <H1>$title</H1>
            </BODY>

</HTML>"

通过创建一个名为title 的变量,并把“System Information Report”字符串赋值给它,我们就可以利用参数展开功能,把这个字符串放到文件中的多个位置。那么,我们怎样来创建一个变量呢?很简单,我们只管使用它。当shell 碰到一个变量的时候,它会自动地创建它。这不同于许多编程语言,它们中的变量在使用之前,必须显式的声明或是定义。关于这个问题,shell 要求非常宽松,这可能会导致一些问题。例如,考虑一下在命令行中发生的这种情形:

[me@linuxbox ~]$ foo="yes"
[me@linuxbox ~]$ echo $foo
yes
[me@linuxbox ~]$ echo $fool
[me@linuxbox ~]$

首先我们把“yes”赋给变量foo,然后用echo 命令来显示变量值。接下来,我们显示拼写错误的变量名“fool”的变量值,然后得到一个空值。这是因为shell 很高兴地创建了变量fool,当shell 遇到fool 的时候,并且赋给fool 一个空的默认值。因此,我们必须小心谨慎地拼写!
同样理解实例中究竟发生了什么事情也很重要。从我们以前学习shell 执行展开操作,我们知道这个命令:

[me@linuxbox ~]$ echo $foo

经历了参数展开操作,然后得到:
[me@linuxbox ~]$ echo yes
然而这个命令:
[me@linuxbox ~]$ echo $fool

展开为:
[me@linuxbox ~]$ echo
这个空变量展开值为空!对于需要参数的命令来说,这会引起混乱。下面是一个例子:

[me@linuxbox ~]$ foo=foo.txt
[me@linuxbox ~]$ foo1=foo1.txt
[me@linuxbox ~]$ cp $foo $fool
cp: missing destination file operand after `foo.txt'
Try `cp --help' for more information.

我们给两个变量赋值,foo 和foo1。然后我们执行cp 操作,但是拼写错了第二个参数的名字。参数展开之后,这个cp 命令只接受到一个参数,虽然它需要两个。
有一些关于变量名的规则:
1. 变量名可由字母数字字符(字母和数字)和下划线字符组成。
2. 变量名的第一个字符必须是一个字母或一个下划线。
3. 变量名中不允许出现空格和标点符号。
单词“variable”意味着可变的值,并且在许多应用程序当中,都是以这种方式来使用变量的。然而,我们应用程序中的变量,title,被用作一个常量。常量有一个名字且包含一个值,在这方面就像是变量。不同之处是常量的值是不能改变的。在执行几何运算的应用程序中,我们可以把PI 定义为一个常量,并把3.1415 赋值给它,用它来代替数字字面值。shell 不能辨别变量和常量;它们大多数情况下是为了方便程序员。一个常用惯例是指定大写字母来表示常量,小写字母表示真正的变量。我们将修改我们的脚本来遵从这个惯例:

#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
echo "<HTML>
            <HEAD>
                        <TITLE>$title</TITLE>

            </HEAD>
            <BODY>
                         <H1>$title</H1>
            </BODY>
</HTML>"

通过在标题中添加shell 变量名HOSTNAME,让标题变得活泼有趣些。这个变量名是这台机器的网络名称。
注意:实际上,shell 确实提供了一种方法,通过使用带有-r(只读)选项的内部命令declare,来强制常量的不变性。如果我们给TITLE 这样赋值:那么shell 会阻止之后给TITLE 的任意赋值。这个功能极少被使用,但为了很早之前的脚本,它仍然存在。

给常量和变量赋值

这里是我们真正开始使用参数扩展知识的地方。正如我们所知道的,这样给变量赋值:

variable=value

这里的variable 是变量的名字,value 是一个字符串。不同于一些其它的编程语言,shell 不会在乎变量值的类型;它把它们都看作是字符串。通过使用带有-i 选项的declare 命令,你可以强制shell 把赋值限制为整型,但是,正如像设置变量为只读一样,极少这样做。注意在赋值过程中,变量名,等号和变量值之间必须没有空格。那么,这些值由什么组成呢?可以展开成字符串的任意值:

a=z # Assign the string "z" to variable a.
b="a string" # Embedded spaces must be within quotes.
c="a string and $b" # Other expansions such as variables can be
# expanded into the assignment.

d=$(ls -l foo.txt) # Results of a command.
e=$((5 * 7)) # Arithmetic expansion.
f="\t\ta string\n" # Escape sequences such as tabs and newlines.
可以在同一行中对多个变量赋值:
a=5 b="a string"
在参数展开过程中,变量名可能被花括号“{}”包围着。由于变量名周围的上下文,其变得不明确的情况下,这会很有帮助。这里,我们试图把一个文件名从myfile 改为myfile1,使
用一个变量:

[me@linuxbox ~]$ filename="myfile"
[me@linuxbox ~]$ touch $filename
[me@linuxbox ~]$ mv $filename $filename1
mv: missing destination file operand after `myfile'
Try `mv --help' for more information.
这种尝试失败了,因为shell 把mv 命令的第二个参数解释为一个新的(并且空的)变量。
通过这种方法可以解决这个问题:
[me@linuxbox ~]$ mv $filename ${filename}1
通过添加花括号,shell 不再把末尾的1 解释为变量名的一部分。
我们将利用这个机会来添加一些数据到我们的报告中,即创建包括的日期和时间,以及创建者的用户名:

#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
echo "<HTML>
              <HEAD>
                          <TITLE>$TITLE</TITLE>
              </HEAD>
              <BODY>
                           <H1>$TITLE</H1>
                           <P>$TIME_STAMP</P>
              </BODY>
</HTML>"

Here Documents

我们已经知道了两种不同的文本输出方法,两种方法都使用了echo 命令。还有第三种方法,叫做here document 或者here script。一个here document 是另外一种I/O 重定向形式,我们在脚本文件中嵌入正文文本,然后把它发送给一个命令的标准输入。它这样工作:

command << token
text
token
这里的command 是一个可以接受标准输入的命令名,token 是一个用来指示嵌入文本结束的字符串。我们将修改我们的脚本,来使用一个here document:


#!/bin/bash
# Program to output a system information page
TITLE="System Information Report For $HOSTNAME"
CURRENT_TIME=$(date +"%x %r %Z")
TIME_STAMP="Generated $CURRENT_TIME, by $USER"
cat << _EOF_
<HTML>
                 <HEAD>
                              <TITLE>$TITLE</TITLE>
                 </HEAD>
                 <BODY>

                              <H1>$TITLE</H1>
                              <P>$TIME_STAMP</P>
                 </BODY>

</HTML>
_EOF_

取代echo 命令,现在我们的脚本使用cat 命令和一个here document。这个字符串EOF(意思是“文件结尾”,一个常见用法)被选作为token,并标志着嵌入文本的结尾。注意这个token 必须在一行中单独出现,并且文本行中没有末尾的空格。那么使用一个here document 的优点是什么呢?它很大程度上和echo 一样,除了默认情况
下,here documents 中的单引号和双引号会失去它们在shell 中的特殊含义。这里有一个命令
中的例子:

[me@linuxbox ~]$ foo="some text"
[me@linuxbox ~]$ cat << _EOF_
> $foo
> "$foo"
> '$foo'
> \$foo
> _EOF_
some text
"some text"
'some text'
$foo

正如所见到的,shell 根本没有注意到引号。它把它们看作是普通的字符。这就允许在一个here document 中可以随意的嵌入引号。对于报告程序来说,这将是非常方便的。Here documents 可以和任意能接受标准输入的命令一块使用。在这个例子中,我们使用了一个here document 将一系列的命令传递到这个ftp 程序中,为的是从一个远端FTP 服务器中得到一个文件:

#!/bin/bash
# Script to retrieve a file via FTP
FTP_SERVER=ftp.nl.debian.org
FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom
REMOTE_FILE=debian-cd_info.tar.gz
ftp -n << _EOF_
open $FTP_SERVER
user anonymous me@linuxbox
cd $FTP_PATH
hash
get $REMOTE_FILE
bye
_EOF_
ls -l $REMOTE_FILE

如果我们把重定向操作符从“<<”改为“<<-”,shell 会忽略在此here document 中开头的tab 字符。这就能缩进一个here document,从而提高脚本的可读性:

#!/bin/bash
# Script to retrieve a file via FTP
FTP_SERVER=ftp.nl.debian.org
FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom
REMOTE_FILE=debian-cd_info.tar.gz
ftp -n <<- _EOF_
      open $FTP_SERVER
      user anonymous me@linuxbox
      cd $FTP_PATH
     hash
      get $REMOTE_FILE
      bye
_EOF_
ls -l $REMOTE_FILE


1 0
原创粉丝点击