[ 上一页 ] [ 目录 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ 14 ] [ 15 ] [ A ] [ 下一页 ]


Debian 参考手册
第 13 章 - 编程


不要用“test”命名可执行的测试文件。test是一个shell的内建命令。


13.1 从哪儿开始

参考资源:

更详细的文档可以从GNU获得打印版本。

接下来的四个小节中包含了用不同的编程语言编写的脚本样例,该脚本创建一个包含用户帐户信息的文本文件,调用一组进程如newusers程序,将这些信息加入到/etc/passwd。每个脚本均需要一个输入文件,该文件应包含格式如first_name last_name password的行。(这些脚本并不创建真正的用户目录。)


13.2 Shell

理解类Unix系统如何工作的最好方法就是阅读shell脚本。在此,我们就shell编程做个简单的介绍。 参阅 Shell Mistakes 来学习更多的错误。


13.2.1 Bash – GNU标准交互式shell

Bash参考资源:

一个简短的程序样例(从标准输入端创建帐户信息供newusers使用):

     #!/bin/bash
     # (C) Osamu Aoki Sun Aug 26 16:53:55 UTC 2001 Public Domain
     pid=1000;
     while read n1 n2 n3 ; do
     if [ ${n1:0:1} != "#" ]; then
     let pid=$pid+1
     echo ${n1}_${n2}:password:${pid}:${pid}:,,,/home/${n1}_${n2}:/bin/bash
     fi
     done

13.2.2 POSIX shells

Debian中有几个软件包提供了POSIX shell:

如果你想编写具有通用性的shell脚本,最好写POSIX shell脚本。可将/bin/sh链接到ash或(dash)来测试脚本的POSIX兼容性。 避免用 bash 化 或 zsh 化的思维去编写脚本。 例如,应避免:

本文档的 shell 描述,只适用于 POSIX 类型的 shell,不适用于包括 tcsh 在内的 csh 类型。


13.2.3 Shell参数

几个需要记住的特殊参数

     $0      = shell名称或shel脚本名称
     $1      = 第一个(1)shell参数
      ...
     $9      = 第九个(9)shell参数
     $#      = 位置参数的个数
     "$*"    = "$1 $2 $3 $4 ... $n"
     "$@"    = "$1" "$2" "$3" "$4" ... "$n"
     $?      = 最近执行的命令的退出状态
     $$      = 当前shell脚本的PID
     $!      = 最近启动的后台作业的PID

需要记住的基本扩展参数

         形式        如果设置了 var      如果没有设置 var
     ${var:-string}  $var                string
     ${var:+string}  string              null
     ${var:=string}  $var                string 
                                         (并且执行var=string)
     ${var:?string}  $var                (返回string然后退出)

在此,冒号“:”在所有运算表达式中事实上均是可选的。

需要记住的替换参数

         形式        	结果
     ${var%suffix}   删除位于var结尾的suffix最小匹配模式
     ${var%%suffix}  删除位于var结尾的suffix最大匹配模式
     ${var#prefix}   删除位于var开头的prefix最小匹配模式
     ${var##prefix}  删除位于var开头的prefix最大匹配模式

13.2.4 Shell重定向

需要记住的基本重定向(redirection)运算符(在此[n]表示定义文件描述符的可选参数):

     [n]> file     重定向标准输出(或 n)到file。
     [n]>> file    重定向标准输出(或 n)到file。
     [n]< filefile重定向到标准输入(或 n)。
     [n1]>&n2      重定向标准输出(或 n1)到n2。
     2> file >&2    重定向标准输出和错误输出到file。
     | command     将标准输出通过管道传递给command。
     2>&1 | command 将标准输出或错误输出通过管道传递给command

在这里:

shell 允许你通过使用 exec 内嵌一个任意的文件描述符来打开文件。

     $ echo Hello >foo
     $ exec 3<foo 4>bar  # 打开文件
     $ cat <&3 >&4       # 重定向标准输入到 3,标准输出到 4
     $ exec 3<&- 4>&-    # 关闭文件
     $ cat bar
     Hello

在这里, n<&-n>&- 表示关闭文件描述符 n


13.2.5 Shell条件表达式

每条命令均可返回一个退出状态,这个状态值可用于条件表达式:

注意该用法,返回值0用来表示“true”与计算机其它领域中常见的转换是不同的。另外`['等阶于使用test命令进行参数赋值`]'相当于一个条件表达式。

需要记住的常用基本条件表达式

     command && if_success_run_this_command_too || true
     command || if_not_success_run_this_command_instead
     
     if [ conditional_expression ]; then  
      if_success_run_this_command
     else
      if_not_success_run_this_command
     fi

当 shell 使用 -e 调用的时候, 需要使用 || true 来确保这个 shell 不会在本行意外退出。

在条件表达式中使用的文件比较运算符有:

     -e file         file存在则返回True。
     -d file         file存在且是一个目录则返回True。
     -f file         如果file存在且是一个普通文件则返回True。
     -w file         如果file存在且可写则返回True。
     -x file         如果file存在且可执行则返回True。
     file1 -nt file2 如果file1file2新则返回True。(指修改日期)
     file1 -ot file2 如果file1file2旧则返回True。(指修改日期)
     file1 -ef file2 如果两者是相同的设备和具有相同的结点(inode)数则返回True。

条件表达式中使用的字符串比较运算符有:

          -z str    如果str长度为零则返回True。
          -n str    如果str长度为非零则返回True。
     str1 == str2   如果字符串相等则返回True。
     str1 =  str2   如果字符串相等则返回True。
         (使用"=="代替"="符合严格意义上的POSIX兼容) 
     str1 != str2   如果字符串不相等则返回True。
     str1 <  str2   如果str1排在str2之前则返回True(与当前位置有关)。
     str1 >  str2   如果str1排在str2之后则返回True(与当前位置有关)。

条件表达式中的算术整数比较运算符有-eq-ne-lt-le-gt-ge


13.2.6 命令行处理

shell按如下的方式处理脚本:

在双单号内单引号将失效。

在 shell 里执行 set -x 或者使用 -x 选项调用 shell, 该 shell 将会显示出所有执行的命令。 这对调试非常有用。


13.3 Awk

Awk的参考资源:

简短的程序样例(创建newusers命令输入):

     #!/usr/bin/awk -f
     # Script to create a file suitable for use in the 'newusers' command,
     # from a file consisting of user IDs and passwords in the form:
     # first_name last_name password
     # Copyright (c) KMSelf Sat Aug 25 20:47:38 PDT 2001
     # Distributed under GNU GPL v 2, or at your option, any later version.
     # This program is distributed WITHOUT ANY WARRANTY.
     
     BEGIN {
         # Assign starting UID, GID
         if ( ARGC > 2 ) {
             startuid = ARGV[1]
             delete ARGV[1]
         }
         else {
             printf( "Usage:  newusers startUID file\n" \
               "  where:\n" \ 
               "    startUID is the starting userid to add, and\n" \
               "    file is an input file in form:\n" \
               "      first_name last_name password\n" \
             )
             exit
         }
     
         infile = ARGV[1]
         printf( "Starting UID: %s\n\n", startuid )
     }
     
     /^#/ { next }
     
     {
         ++record
         first = $1
         last = $2
         passwd = $3
         user= substr( tolower( first ), 1, 1 ) tolower( last )
         uid = startuid + record - 1
         gid = uid
         printf( "%s:%s:%d:%d:%s %s,,/home/%s:/bin/bash\n",  \
             user, passwd, uid, gid, first, last, user \
             )
     }

Debian中有两个软件包提供了POSIX awk


13.4 Perl

运行于类Unix系统上的解释器

Perl参考资源:

简短的程序样例(创建newusers命令输入):

     #!/usr/bin/perl
     # (C) Osamu Aoki Sun Aug 26 16:53:55 UTC 2001 Public Domain
     $pid=1000;
     while (<STDIN>) {
             if (/^#/) { next;}
             chop;
             $pid++;
             ($n1, $n2, $n3) = split / /;
             print $n1,"_",$n2,":", $n3, ":",$pid,
                       ":",$pid,",,,/home/",$n1,"_",$n2,":/bin/bash\n"
     }

安装Perl模块module_name

     # perl -MCPAN -e 'install module_name'

13.5 Python

一个不错的面向对象的解释器。

Python参考资源:

简短的程序样例(创建newusers命令输入):

     #! /usr/bin/env python
     import sys, string
     
     # (C) Osamu Aoki Sun Aug 26 16:53:55 UTC 2001 Public Domain
     # Ported from awk script by KMSelf Sat Aug 25 20:47:38 PDT 2001
     # This program is distributed WITHOUT ANY WARRANTY.
     
     def usages():
         print \
     "Usage:  ", sys.argv[0], " start_UID [filename]\n" \
     "\tstartUID is the starting userid to add.\n" \
     "\tfilename is input filename. If not specified, standard input.\n\n" \
     "Input file format:\n"\
     "\tfirst_name last_name password\n"
                     return 1
     
     def parsefile(startuid):
         #
         # main filtering
         #
         uid = startuid
         while 1:
             line = infile.readline()
             if not line:
                 break
             if line[0] == '#':
                 continue
             (first, last, passwd) = string.split(string.lower(line))
             # above crashes with wrong # of parameters :-)
             user = first[0] + last
             gid = uid
             lineout = "%s:%s:%d:%d:%s %s,,/home/%s:/bin/bash\n" %  \
                 (user, passwd, uid, gid, first, last, user)
             sys.stdout.write(lineout)
             +uid
     
     if __name__ == '__main__':
         if len(sys.argv) == 1:
             usages()
         else:
             uid = int(sys.argv[1])
             #print "# UID start from: %d\n" % uid
             if len(sys.argv) > 1:
                 infilename   = string.join(sys.argv[2:])
                 infile = open(infilename, 'r')
                 #print "# Read file from: %s\n\n" % infilename
             else:
                 infile = sys.stdin
             parsefile(uid)

13.6 Make

Make参考资源:

简单自动变量:

语法规则:

     target: [ prerequisites ... ]
      [TAB]  command1
      [TAB]  -command2 # ignore errors
      [TAB]  @command3 # suppress echoing

在此[TAB]代表一个TAB符。 在完成 make 变量代换后,shell将逐行进行解释。在行尾使用\可以续行。使用$$可将$加入到shell脚本的环境变量中。

适用于targetprerequisites的隐含的等价规则:

     %: %.c header.h

or,

     %.o: %.c header.h

在此,target包含了%字符(确切地说是其中之一),%可匹配实际的target文件名中任何非空子串。prerequisites同样也使用%来显示它们的名字是如何关联到实际的target文件名的。

Suffix rules方法来定义make的隐含规则(implicit rules)已经过时。GNU make因为兼容性的考虑仍支持它,但只要有可能就应该使用与之等价的模版规则(pattern rules):

     old suffix rule --> new pattern rule
     .c:             --> %  : %.c
     .c.o:           --> %.o: %.c

上述规则所使用的自动变量:

     foo.o: new1.c new2.c old1.c new3.c
     $@ == foo.o                         (target)
     $< == new1.c                        (first one)
     $? == new1.c new2.c new3.c          (newer ones)
     $^ == new1.c new2.c old1.c new3.c   (all)
     $* == `%' matched stem in the target pattern.

变量参考:

     foo1 := bar    # One-time expansion
     foo2  = bar    # Recursive expansion
     foo3 += bar    # Append
     SRCS := $(wildcard *.c)
     OBJS := $(foo:c=o)
     OBJS := $(foo:%.c=%.o) 
     OBJS := $(patsubst %.c,%.o,$(foo)) 
     DIRS  = $(dir directory/filename.ext) # Extracts "directory"
      $(notdir NAMES...), $(basename NAMES...), $(suffix NAMES...) ...

执行make -p -f/dev/null可查看内部自动规则。


13.7 C

准备工作:

     # apt-get install glibc-doc manpages-dev libc6-dev gcc

C参考资源:


13.7.1 简单C编程(gcc)

一个简单的例子,将example.c和库函数libm编译成可执行文件run_example

     $ cat > example.c << EOF
     #include <stdio.h>
     #include <math.h>
     #include <string.h>
     
     int main(int argc, char **argv, char **envp){
             double x;
             char y[11];
             x=sqrt(argc+7.5);
             strncpy(y, argv[0], 10); /* prevent buffer overflow */
             y[10] = '\0'; /* fill to make sure string ends with '\0' */
             printf("%5i, %5.3f, %10s, %10s\n", argc, x, y, argv[1]);
             return 0;
     }
     EOF
     $ gcc -Wall -g -o run_example example.c -lm
     $ ./run_example
             1, 2.915, ./run_exam,     (null)
     $ ./run_example 1234567890qwerty
             2, 3.082, ./run_exam, 1234567890qwerty

在此,sqrt() 链接库函数libm需要-lm选项。真正的库函数是位于/lib/下的libm.so.6,它是libm-2.1.3.so的一个符号链接。

看看输出文本中最后的参数,尽管指定了%10s,它还是多于10个字符。

使用不带边界检查的指针内存操作函数如sprintfstrcpy会妨碍缓冲区溢出侦测,故使用snprintfstrncpy


13.7.2 调试


13.7.2.1 使用gdb进行调试

准备工作:

     # apt-get install gdb

gdb参考资源:

使用-g选项编译程序就可使用gdb进行调试。许多命令都可以缩写。Tab扩展功能和在shell中的一样。

     $ gdb program
     (gdb) b 1                # 在line 1设置断点
     (gdb) run arg1 arg2 arg3 # 运行程序
     (gdb) next               # 下一行
     ...
     (gdb) step               # 前进一步
     ...
     (gdb) p parm             # 打印parm  
     ...
     (gdb) p parm=12          # 设置其值为12

在Emacs环境下调试程序,参阅编辑器命令总汇(Emacs,Vim), 第 11.3.4 节

Debian 系统上所有默认安装的二进制文件都已经进行了 strip 操作, 调试符号已经被移除。 为了能够让 gdb 对 Debian 软件包进行调试, 相关的软件包需要使用下面的方法重新打包:

更多信息请参阅: Policy 10.1


13.7.2.2 检查库函数关联关系

使用ldd可查看程序与库函数的关联关系:

     $ ldd /bin/ls
             librt.so.1 => /lib/librt.so.1 (0x4001e000)
             libc.so.6 => /lib/libc.so.6 (0x40030000)
             libpthread.so.0 => /lib/libpthread.so.0 (0x40153000)
             /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

可在chrooted环境下使用ls检查上述库函数在你的chroot环境中是否可见。

下面的命令也很有用:


13.7.2.3 使用内存查漏工具进行调试

Debian中有几个内存查漏工具。

亦可查阅Debugging Tools for Dynamic Storage Allocation and Memory Management.


13.7.3 Flex – 更好的Lex

flex是一个快速的词法分析机生成器。

flex参考资源:

需要提供你自己的main()yywrap(),或者你的 program.l 象这样不带library编译(yywrap是一个宏;%option main隐含地打开了%option noyywrap):

     %option main
     %%
     .|\n    ECHO ;
     %%

另外,还可以在cc命令行末尾加上-lfl链接选项进行编译(象AT&T-Lex使用-ll一样),此时就不需要%option了。


13.7.4 Bison – 更好的Yacc

Debian中有几个软件包提供了与Yacc兼容的LALR词法生成器:

bison参考资源:

需要提供自己的main()yyerror()main()调用yyparse(),而yyparse()调用yylex(),通常由FleX创建。

     %%
     
     %%

13.7.5 Autoconf

autoconf一个shell脚本生成工具,由它生成的脚本能自动配置软件源码包,以适用于各种使用全套GNU build系统的类UNIX系统。

autoconf会生成配置脚本configureconfigure使用 Makefile.in模版自动创建自定义Makefile


13.7.5.1 编译并安装程序

Debian不会改动/usr/local/下的文件(参阅多样性支持, 第 2.5 节)。所以如果是从源码编译程序,并将其安装到/usr/local/下,是不会影响到Debian的。

     $ cd src
     $ ./configure --prefix=/usr/local
     $ make
     $ make install # this puts the files in the system

13.7.5.2 卸载程序

如果仍保存有源码,对其使用了autoconf/automake,并且记得是如何进行配置的:

     $ ./configure all-of-the-options-you-gave-it
     # make uninstall

另一种方法是,如果可以确定安装过程将文件都放在了/usr/local/,并且该目录下没有什么别的重要文件,可用下面的命令将其全部删除:

     # find /usr/local -type f -print0 | xargs -0 rm -f

如果不能确定文件安装到什么位置,最好使用checkinstall,该命令可提供明确的卸载路径。


13.8 Web

通过下面的方法来制作一个基本的交互动态网页:

出于安全的考虑,最好不要手工写新的程序来分析 CGI 参数。 在 Perl (参阅 Perl, 第 13.4 节)、Python (参阅 Python, 第 13.5 节) 和 PHP 中,有即定的模块具备这些功能。 当需要在客户端存储数据的时候,使用 cookies。 当需要在客户端进行数据处理的时候,经常使用 javascript。

更多信息,请参阅 The Common Gateway InterfaceThe Apache Software FoundationJavaScript

在浏览器地址栏里面直接输入编码的 URL http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial,可以在 Google 搜索 “CGI tutorial”。 这也是一个查看 CGI 脚本在 Google 服务器上执行的好方法。


13.9 准备文档


13.9.1 roff排版

传统上,roff是主要的Unix文字处理系统。

参阅roff(7)groff(7)groff(1)grotty(1)troff(1)groff_mdoc(7)groff_man(7)groff_ms(7)groff_me(7)groff_mm(7)以及info groff

-me宏提供了一个不错了说明文档。如果使用的是groff(1.18或更新的版本),找到/usr/share/doc/groff/meintro.me.gz并执行下面的命令:

     $ zcat /usr/share/doc/groff/meintro.me.gz | \
          groff -Tascii -me - | less -R

下面的命令将生成一个完整的纯文本文件:

     $ zcat /usr/share/doc/groff/meintro.me.gz | \
         GROFF_NO_SGR=1 groff -Tascii -me - | col -b -x > meintro.txt

如果想打印出来,可使用PostScript输出:

     $ groff -Tps meintro.txt | lpr
     $ groff -Tps meintro.txt | mpage -2 | lpr

13.9.2 SGML

准备工作:

     # apt-get install debiandoc-sgml debiandoc-sgml-doc

debiandoc-sgml参考资源:

SGML能管理多种格式的文档。更简单的SGML系统是Debiandoc,本文档就使用到它完成的。只需对原始的文本文件的下列字符进行少许转换:

设置章节为非打印注释,输入:

     <!-- State issue here ... -->

设置章节为可控注释,输入:

     <![ %FIXME; [ State issue here ... ]]>

在SGML中,仅条目的首次声明(first definition)有效。例如:

     <!entity % qref "INCLUDE">
     <![ %qref; [ <!entity param "Data 1"> ]]>
     <!entity param "Data 2">
     &param;

最终结果是“Data 1”。如果第一行使用“IGNORE”而非“INCLUDE”,则最终结果为“Data 2”(第二行是一个候选声明)。同样,重复出现的短语可分别提前在文档中定义。

     <!entity whoisthis "my">
     Hello &whoisthis; friend.
     This is &whoisthis; book.

该定义的结果如下:

     Hello my friend.
     This is my book.

可参阅examples目录中简短的SGML样例文件sample.sgml

当 SGML 文档逐渐变大时,作为后端文本处理器使用的 TeX 偶尔会错误。 参阅 TeX/LaTeX, 第 13.9.3 节


13.9.3 TeX/LaTeX

准备:

     # tasksel # select Miscellaneous  --> TeX/LaTeX environment

LaTeX 参考:

这是一个很强大的排字环境。许多 SGML 处理器使用 LaTeX 作为他们的后端文本处理器。 由 lyxlyx-xformslyx-qt 提供的 Lyx,以及由 texmacs 提供的 GNU TeXmacs 为 LaTeX 提供了一个好的“所见及所得”的编辑环境,然而,许多人选择使用 Emacs 和 Vim 作为源代码编辑器。

有许多在线资源存在:

当SGML文档不断增大后,TeX偶尔会出错。可通过修改/etc/texmf/texmf.cnf,增加缓冲池的容量来解决这个问题(更好的方法是编辑/etc/texmf/texmf.d/95NonPath然后运行update-texmf)。


13.9.4 文学编程

有文学的程序员书写包含代码的文档来代替包含文档的代码。 这种方法确保程序有一个好的文档。

关于文学编程的更多信息,参阅 Literate Programming


13.9.4.1 Noweb

准备:

     # apt-get install nowebm

Noweb 参考:

这是一个类 WEB 的文学编程工具,独立于编程语言。由于提供了扩展而更简单。 [68] 当 noweb 被调用的时候,它将程序的源代码输出到在 noweb 中提到的文件中,它还将创建一个用于文档排版的 TeX 文件。

Debian ifupdown 软件包是一个很好的例子。

     $ apt-get source ifupdown
     $ cd ifupdown*
     $ make ifupdown.pdf ifupdown.ps

13.9.4.2 Doxygen

准备:

     # apt-get install doxygen doxygen-doc doxygen-gui

Doxygen 参考(由 doxygen 创建):

它能够为 C++、C、 Java、 IDL 和某些范围的 PHP 和 C# 程序产生 HTML、 RTF、 Unix 手册页、 PostScript 和 PDF(使用 LaTeX)文档。 Doxygen 与 JavaDoc (1.1)、 Qt-Doc 和 KDOC 兼容,它有特定的设计用来与用 Troll Tech 的 Qt 工具包制作的项目兼容。 他甚至可以为没有文档的程序创建从属图、协作图和图形化的类分层图。输出类似于 Qt 的文档。


13.10 打包

准备工作:

     # apt-get install debian-policy developers-reference \
                       maint-guide dh-make debhelper
     # apt-get install packaging-manual # if Potato

有关打包的参考资源:


13.10.1 单个二进制文件打包

Joey Hess的快速和粗糙的打包法:生成一个单独的二进制包

     # mkdir -p mypkg/usr/bin mypkg/DEBIAN
     # cp binary mypkg/usr/bin
     # cat > mypkg/DEBIAN/control
     Package: mypackage
     Version: 1
     Architecture: i386
     Maintainer: Joey Hess <joeyh@debian.org>
     Description: my little package
      Don't expect much.
     ^D
     # dpkg-deb -b mypkg

13.10.2 使用工具打包

使用dh_make软件包中的dh-make工具创建一个基线包,接着按照dh-make(1)中描述的方法打包。会用到debian/rules中的debhelper

一个较老的方法是使用debmake软件包中的deb-make。不需要debhelper脚本,仅需要shell环境。

有关多种源码包的例子,参阅“mc”(dpkg-source -x mc_4.5.54.dsc),其中用到Adam Heath(doogie@debian.org)的“sys-build.mk”以及“glibc”(dpkg-source -x glibc_2.2.4-1.dsc)它由后来的Joel Klecker(espy@debian.org)所写的另一个系统打包。


[ 上一页 ] [ 目录 ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ 13 ] [ 14 ] [ 15 ] [ A ] [ 下一页 ]


Debian 参考手册

1.09-1ubuntu1, 星期日 五月 14 02:44:32 UTC 2006

Osamu Aoki osamu@debian.org
译者:
Hao "Lyoo" Liu iamlyoo@163.net
Ming Hua minghua@rice.edu
肖盛文 atzlinux@163.com
Haifeng Chen optical.dlz@gmail.com
解彦博 xieyanbo@gmail.com
easthero easthero@gmail.com
作者, 第 A.1 节