第 12 章。 程序设计

目录

12.1. Shell 脚本
12.1.1. POSIX Shell 兼容性
12.1.2. Shell 参数
12.1.3. Shell 条件语句
12.1.4. Shell 循环
12.1.5. Shell 命令行处理顺序
12.1.6. Shell 脚本的实用工具程序
12.1.7. Shell 脚本对话框
12.1.8. 使用 zenity 的 Shell 脚本示例
12.2. Make
12.3. C
12.3.1. 简单的 C 程序 (gcc)
12.4. 调试
12.4.1. 基本的 gdb 执行
12.4.2. 调试 Debian 软件包
12.4.3. 获取回溯
12.4.4. 高级 gdb 命令
12.4.5. 调试 X 错误
12.4.6. 检查库的依赖关系
12.4.7. 内存泄漏检测工具
12.4.8. 静态代码分析工具
12.4.9. 反汇编二进制文件
12.5. Flex — 更好的 Lex
12.6. Bison — 更好的 Yacc
12.7. Autoconf
12.7.1. 编译和安装程序
12.7.2. 卸载程序
12.8. Perl 短脚本狂热
12.9. Web
12.10. 源代码翻译
12.11. 制作 Debian 软件包

我为人们学习在 Debian 系统上进行程序设计提供了一些指导,足以追踪打包的源代码。以下是值得注意的软件包和相应的程序设计文档软件包。

表 12.1. 帮助程序设计的软件包列表

软件包 popcon 大小 文档
autoconf http://qa.debian.org/popcon.php?package=autoconf 1896 autoconf-doc 提供的“info autoconf
automake http://qa.debian.org/popcon.php?package=automake 1530 automake1.10-doc 提供的“info automake
bash http://qa.debian.org/popcon.php?package=bash 3935 bash-doc 提供的“info bash
bison http://qa.debian.org/popcon.php?package=bison 1518 bison-doc 提供的“info bison
cpp http://qa.debian.org/popcon.php?package=cpp 63 cpp-doc 提供的“info cpp
ddd http://qa.debian.org/popcon.php?package=ddd 3636 ddd-doc 提供的“info ddd
exuberant-ctags http://qa.debian.org/popcon.php?package=exuberant-ctags 323 exuberant-ctags(1)
flex http://qa.debian.org/popcon.php?package=flex 1352 flex-doc 提供的“info flex
gawk http://qa.debian.org/popcon.php?package=gawk 2061 gawk-doc 提供的“info gawk
gcc http://qa.debian.org/popcon.php?package=gcc 41 gcc-doc 提供的“info gcc
gdb http://qa.debian.org/popcon.php?package=gdb 6447 gdb-doc 提供的“info gdb
gettext http://qa.debian.org/popcon.php?package=gettext 6355 gettext-doc 提供的“info gettext
gfortran http://qa.debian.org/popcon.php?package=gfortran 33 gfortran-doc (Fortran 95) 提供的“info gfortran
fpc http://qa.debian.org/popcon.php?package=fpc 37 fpc(1) 和由 fp-docs (Pascal) 提供的 html
glade http://qa.debian.org/popcon.php?package=glade 1752 通过菜单提供的帮助 (UI Builder)
libc6 http://qa.debian.org/popcon.php?package=libc6 9500 glibc-docglibc-doc-reference 提供的“info libc
make http://qa.debian.org/popcon.php?package=make 1145 make-doc 提供的“info make
xutils-dev http://qa.debian.org/popcon.php?package=xutils-dev 1421 imake(1), xmkmf(1), 等。
mawk http://qa.debian.org/popcon.php?package=mawk 198 mawk(1)
perl http://qa.debian.org/popcon.php?package=perl 17077 perl-docperl-doc-html 提供的 perl(1) 和 html 页面
python http://qa.debian.org/popcon.php?package=python 655 python-doc 提供的 python(1) 和 html 页面
tcl8.4 http://qa.debian.org/popcon.php?package=tcl8.4 3332 tcl8.4-doc 提供的 tcl(3) 和详细手册页
tk8.4 http://qa.debian.org/popcon.php?package=tk8.4 2712 tk8.4-doc 提供的 tk(3) 和详细手册页
ruby http://qa.debian.org/popcon.php?package=ruby 36 ri 提供的 ruby(1) 和交互式参考
vim http://qa.debian.org/popcon.php?package=vim 1873 vim-doc 提供的帮助 (F1) 菜单
susv2 http://qa.debian.org/popcon.php?package=susv2 48 获取 “The Single Unix Specifications v2
susv3 http://qa.debian.org/popcon.php?package=susv3 48 获取 “The Single Unix Specifications v3

通过安装 manpagesmanpages-dev 软件包后,输入 “man name” 可以获得在线参考。通过安装相关的文档软件包后,输入 “info program_name” 可以获得 GNU 工具的在线参考。 您可能需要在 main 存档之外,还包括 contribnon-free 存档,因为一些 GFDL 文档不被认为是 DFSG 兼容的。

[Warning] 警告

不要使用 “test” 作为可执行测试文件的名称。“test” 是 shell 内置命令。

[Caution] 注意

您应该将直接从源代码编译的软件程序安装到 “/usr/local” 或 “/opt” 中,以避免与系统程序冲突。

[Tip] 提示

创建 “啤酒之歌 99 瓶” 的代码示例 应该能让您很好地了解几乎所有程序设计语言。

12.1. Shell 脚本

Shell 脚本 是一个设置了执行位的文本文件,包含以下格式的命令。

#!/bin/sh
 ... command lines

第一行指定了读取和执行此文件内容的 shell 解释器。

阅读 shell 脚本是理解类 Unix 系统如何工作的最佳方式。在这里,我给出一些 shell 程序设计的提示和提醒。请参阅 “Shell 错误” (http://www.greenend.org.uk/rjk/2001/04/shell.html) 以从错误中学习。

与 shell 交互模式(请参阅 第 1.5 节,“简单的 Shell 命令”第 1.6 节,“类 Unix 文本处理”)不同,shell 脚本经常使用参数、条件语句和循环。

12.1.1. POSIX Shell 兼容性

许多系统脚本可以由任何 POSIX shell 解释(请参阅 表 1.13,“Shell 程序列表”)。系统的默认 shell 是 “/bin/sh”,它是一个指向实际程序的符号链接。

  • bash(1) 用于 lenny 或更旧版本

  • dash(1) 用于 squeeze 或更新版本

避免编写带有 bashismszshisms 的 shell 脚本,以使其在所有 POSIX shell 之间可移植。您可以使用 checkbashisms(1) 进行检查。

表 12.2. 典型的 bashisms 列表

良好:POSIX 避免:bashism
if [ "$foo" = "$bar" ] ; then … if [ "$foo" == "$bar" ] ; then …
diff -u file.c.orig file.c diff -u file.c{.orig,}
mkdir /foobar /foobaz mkdir /foo{bar,baz}
funcname() { … } function funcname() { … }
八进制格式: “\377 十六进制格式: “\xff

必须谨慎使用 “echo” 命令,因为其实现因 shell 内置命令和外部命令而异。

  • 避免使用命令选项 “-e” 和 “-E”。

  • 除了 “-n” 之外,避免使用任何命令选项。

  • 避免在字符串中使用转义序列,因为它们的处理方式各不相同。

[Note] 注意

虽然 “-n” 选项并非真正的 POSIX 语法,但它通常被接受。

[Tip] 提示

如果需要在输出字符串中嵌入转义序列,请使用 “printf” 命令而不是 “echo” 命令。

12.1.2. Shell 参数

shell 脚本中经常使用特殊的 shell 参数。

表 12.3. Shell 参数列表

shell 参数
$0 shell 或 shell 脚本的名称
$1 第一个 (1) shell 参数
$9 第九个 (9) shell 参数
$# 位置参数的数量
"$*" “$1 $2 $3 $4 …”
"$@" “$1” “$2” “$3” “$4” …
$? 最近命令的退出状态
$$ 此 shell 脚本的 PID
$! 最近启动的后台作业的 PID

需要记住的基本参数扩展如下。

表 12.4. Shell 参数扩展列表

参数表达式形式 如果 var 已设置的值 如果 var 未设置的值
${var:-string} $var string
${var:+string} string null
${var:=string} $var string”(并运行 “var=string”)
${var:?string} $var stderr 输出 “string”(并以错误退出)

这里,所有这些运算符中的冒号 “:” 实际上是可选的。

  • :” = 运算符测试存在非空

  • 不带:” = 运算符仅测试存在

表 12.5. 关键 shell 参数替换列表

参数替换形式 结果
${var%suffix} 删除最短后缀模式
${var%%suffix} 删除最长后缀模式
${var#prefix} 删除最短前缀模式
${var##prefix} 删除最长前缀模式

12.1.3. Shell 条件语句

每个命令都返回一个退出状态,可用于条件表达式。

  • 成功:0 (“真”)

  • 错误:非 0 (“假”)

[Note] 注意

shell 条件上下文中的 “0” 表示 “真”,而 C 条件上下文中的 “0” 表示 “假”。

[Note] 注意

[” 等同于 test 命令,它将 “]” 之前的参数评估为条件表达式。

需要记住的基本条件惯用法如下。

  • <command> && <if_success_run_this_command_too> || true

  • <command> || <if_not_success_run_this_command_too> || true

  • 如下所示的多行脚本片段

if [ <conditional_expression> ]; then
 <if_success_run_this_command>
else
 <if_not_success_run_this_command>
fi

这里需要尾部的 “|| true” 以确保当 shell 使用 “-e” 标志调用时,此 shell 脚本不会在此行意外退出。

表 12.6. 条件表达式中的文件比较运算符列表

等式 返回逻辑真的条件
-e <file> <file> 存在
-d <file> <file> 存在且是一个目录
-f <file> <file> 存在且是一个常规文件
-w <file> <file> 存在且可写
-x <file> <file> 存在且可执行
<file1> -nt <file2> <file1> 比 <file2> 新 (修改时间)
<file1> -ot <file2> <file1> 比 <file2> 旧 (修改时间)
<file1> -ef <file2> <file1> 和 <file2> 在同一设备上且具有相同的 inode 号

表 12.7. 条件表达式中的字符串比较运算符列表

等式 返回逻辑真的条件
-z <str> <str> 的长度为零
-n <str> <str> 的长度为非零
<str1> = <str2> <str1> 和 <str2> 相等
<str1> != <str2> <str1> 和 <str2> 不相等
<str1> < <str2> <str1> 排序在 <str2> 之前 (取决于区域设置)
<str1> > <str2> <str1> 排序在 <str2> 之后 (取决于区域设置)

条件表达式中的算术整数比较运算符为 “-eq”、“-ne”、“-lt”、“-le”、“-gt” 和 “-ge”。

12.1.4. Shell 循环

POSIX shell 中有几种循环惯用法可以使用。

  • for x in foo1 foo2 … ; do command ; done” 通过将列表 “foo1 foo2 …” 中的项目分配给变量 “x” 并执行 “command” 来循环。

  • while condition ; do command ; done” 在 “condition” 为真时重复 “command”。

  • until condition ; do command ; done” 在 “condition” 不为真时重复 “command”。

  • break” 允许退出循环。

  • continue” 允许恢复循环的下一次迭代。

[Tip] 提示

C 语言的数字迭代可以通过使用 seq(1) 作为 “foo1 foo2 …” 生成器来实现。

12.1.5. Shell 命令行处理顺序

shell 大致按以下顺序处理脚本。

  • shell 读取一行。

  • 如果在一行的一部分位于 “…”‘…’ 内,则 shell 将其分组为一个标记

  • shell 通过以下方式将行的其他部分拆分为标记

    • 空白字符:<空格> <制表符> <换行符>

    • 元字符:< > | ; & ( )

  • 如果不在 “…”‘…’ 内,则 shell 检查每个标记的保留字以调整其行为。

    • 保留字if then elif else fi for in while unless do done case esac

  • 如果不在 “…”‘…’ 内,则 shell 扩展别名

  • 如果不在 “…”‘…’ 内,则 shell 扩展波浪号

    • ~” → 当前用户的家目录

    • ~<user>” → <user> 的家目录

  • 如果不在 ‘…’ 内,则 shell 将参数扩展为其值。

    • 参数:“$PARAMETER” 或 “${PARAMETER}

  • 如果不在 ‘…’ 内,则 shell 扩展命令替换

    • $( command )” → “command” 的输出

    • ` command `” → “command” 的输出

  • 如果不在 “…”‘…’ 内,则 shell 将路径名通配符扩展为匹配的文件名。

    • * → 任意字符

    • ? → 一个字符

    • […] → “” 中的任意一个字符

  • shell 从以下位置查找命令并执行它。

    • 函数定义

    • 内置命令

    • $PATH” 中的可执行文件

  • shell 转到下一行,并从该序列的顶部再次重复此过程。

双引号内的单引号无效。

在 shell 中执行 “set -x” 或使用 “-x” 选项调用 shell 会使 shell 打印所有执行的命令。 这对于调试非常方便。

12.1.6. Shell 脚本的实用工具程序

为了使您的 shell 程序在 Debian 系统中尽可能地可移植,最好将实用工具程序限制为 essential 软件包提供的程序。

  • aptitude search ~E” 列出 essential 软件包。

  • dpkg -L <package_name> |grep '/man/man.*/'” 列出 <package_name> 软件包提供的命令的手册页。

表 12.8. 包含用于 shell 脚本的小型实用工具程序的软件包列表


[Tip] 提示

虽然 moreutils 可能在 Debian 之外不存在,但它提供了有趣的小程序。最值得注意的是 sponge(8),当您希望覆盖原始文件时,它非常有用。

12.1.7. Shell 脚本对话框

可以通过使用所谓的对话框程序等,将简单 shell 程序的用户界面从通过 echoread 命令进行的枯燥交互改进为更具交互性的界面。

表 12.9. 用户界面程序列表

软件包 popcon 大小 描述
x11-utils http://qa.debian.org/popcon.php?package=x11-utils 554 xmessage(1):在窗口中显示消息或查询 (X)
whiptail http://qa.debian.org/popcon.php?package=whiptail 87 从 shell 脚本显示用户友好的对话框 (newt)
dialog http://qa.debian.org/popcon.php?package=dialog 1230 从 shell 脚本显示用户友好的对话框 (ncurses)
zenity http://qa.debian.org/popcon.php?package=zenity 324 从 shell 脚本显示图形对话框 (gtk2.0)
ssft http://qa.debian.org/popcon.php?package=ssft 152 Shell 脚本前端工具(zenity、kdialog 和 dialog 的包装器,带有 gettext)
gettext http://qa.debian.org/popcon.php?package=gettext 6355 /usr/bin/gettext.sh”:翻译消息

12.1.8. 使用 zenity 的 Shell 脚本示例

这是一个简单的脚本,它使用 RS02 数据创建 ISO 镜像,并由 dvdisaster(1) 补充。

#!/bin/sh -e
# gmkrs02 : Copyright (C) 2007 Osamu Aoki <osamu@debian.org>, Public Domain
#set -x
error_exit()
{
  echo "$1" >&2
  exit 1
}
# Initialize variables
DATA_ISO="$HOME/Desktop/iso-$$.img"
LABEL=$(date +%Y%m%d-%H%M%S-%Z)
if [ $# != 0 ] && [ -d "$1" ]; then
  DATA_SRC="$1"
else
  # Select directory for creating ISO image from folder on desktop
  DATA_SRC=$(zenity --file-selection --directory  \
    --title="Select the directory tree root to create ISO image") \
    || error_exit "Exit on directory selection"
fi
# Check size of archive
xterm -T "Check size $DATA_SRC" -e du -s $DATA_SRC/*
SIZE=$(($(du -s $DATA_SRC | awk '{print $1}')/1024))
if [ $SIZE -le 520 ] ; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for CD backup:\\n $SIZE MB"
elif [ $SIZE -le 3500 ]; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for DVD backup :\\n $SIZE MB"
else
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is too big to backup : $SIZE MB"
  error_exit "The data size is too big to backup :\\n $SIZE MB"
fi
# only xterm is sure to have working -e option
# Create raw ISO image
rm -f "$DATA_ISO" || true
xterm -T "genisoimage $DATA_ISO" \
  -e genisoimage -r -J -V "$LABEL" -o "$DATA_ISO" "$DATA_SRC"
# Create RS02 supplemental redundancy
xterm -T "dvdisaster $DATA_ISO" -e  dvdisaster -i "$DATA_ISO" -mRS02 -c
zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
  --text="ISO/RS02 data ($SIZE MB) \\n created at: $DATA_ISO"
# EOF

您可能希望在桌面上创建一个启动器,命令设置为类似 “/usr/local/bin/gmkrs02 %d”。

12.2. Make

Make 是一个用于维护程序组的实用工具。 执行 make(1) 后,make 读取规则文件 “Makefile”,如果目标依赖的先决条件文件自上次修改目标以来已被修改,或者如果目标不存在,则更新目标。 这些更新的执行可能会并发发生。

规则文件语法如下。

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

这里 “ [TAB] ” 是一个 TAB 代码。 每行在 make 变量替换后由 shell 解释。 在行尾使用 “\” 以继续脚本。 使用 “$$” 为 shell 脚本输入 “$” 的环境变量值。

可以编写目标和先决条件的隐式规则,例如,通过以下方式。

%.o: %.c header.h

这里,目标包含字符 “%”(恰好一个)。 “%” 可以匹配实际目标文件名中的任何非空子字符串。 先决条件也类似地使用 “%” 来显示其名称与实际目标名称的关系。

表 12.10. make 自动变量列表

自动变量
$@ 目标
$< 第一个先决条件
$? 所有较新的先决条件
$^ 所有先决条件
$* 目标模式中匹配的 “%” 词干

表 12.11. make 变量扩展列表

变量扩展 描述
foo1 := bar 一次性扩展
foo2 = bar 递归扩展
foo3 += bar 追加

运行 “make -p -f/dev/null” 以查看自动内部规则。

12.3. C

您可以通过以下方式设置适当的环境来编译用 C 程序设计语言 编写的程序。

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

libc6-dev 软件包,即 GNU C 库,提供了 C 标准库,它是 C 程序设计语言使用的一组头文件和库例程。

有关 C 的参考资料如下。

  • info libc” (C 库函数参考)

  • gcc(1) 和 “info gcc

  • 每个 C 库函数名称(3)

  • Kernighan & Ritchie, “The C Programming Language”, 第 2 版 (Prentice Hall)

12.3.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

这里,需要 “-lm” 来链接来自 libc6 软件包的库 “/usr/lib/libm.so”,用于 sqrt(3)。 实际库位于 “/lib/” 中,文件名为 “libm.so.6”,它是指向 “libm-2.7.so” 的符号链接。

查看输出文本中的最后一个参数。 即使指定了 “%10s”,也有超过 10 个字符。

不建议使用不带边界检查的指针内存操作函数,例如 sprintf(3)strcpy(3),以防止利用上述溢出效应的缓冲区溢出漏洞。 请改用 snprintf(3)strncpy(3)

12.4. 调试

调试是程序设计活动的重要组成部分。 了解如何调试程序使您成为一个优秀的 Debian 用户,可以生成有意义的错误报告。

12.4.1. 基本的 gdb 执行

Debian 上的主要 调试器gdb(1),它使您可以在程序执行时检查程序。

让我们通过以下方式安装 gdb 和相关程序。

# apt-get install gdb gdb-doc build-essential devscripts

info gdb” 提供了 gdb 的良好教程,或者在 网络上的其他地方 也可以找到。 以下是在使用 “-g” 选项编译的 “program” 上使用 gdb(1) 以生成调试信息的简单示例。

$ gdb program
(gdb) b 1                # set break point at line 1
(gdb) run args           # run program with args
(gdb) next               # next line
...
(gdb) step               # step forward
...
(gdb) p parm             # print parm
...
(gdb) p parm=12          # set value to 12
...
(gdb) quit
[Tip] 提示

许多 gdb(1) 命令可以缩写。 Tab 扩展的工作方式与 shell 中相同。

12.4.2. 调试 Debian 软件包

由于默认情况下所有已安装的二进制文件都应该在 Debian 系统上被剥离,因此大多数调试符号都在普通软件包中被删除。 为了使用 gdb(1) 调试 Debian 软件包,需要安装相应的 *-dbg 软件包(例如,libc6 的情况下的 libc6-dbg)。

如果要调试的软件包未提供其 *-dbg 软件包,则需要在通过以下方式重建后安装它。

$ mkdir /path/new ; cd /path/new
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ sudo apt-get build-dep source_package_name
$ apt-get source package_name
$ cd package_name*

如果需要,修复错误。

将软件包版本提升为与官方 Debian 版本不冲突的版本,例如,当重新编译现有软件包版本时,附加 “+debug1”,或者当编译未发布的软件包版本时,附加 “~pre1”,如下所示。

$ dch -i

通过以下方式编译和安装带有调试符号的软件包。

$ export DEB_BUILD_OPTIONS=nostrip,noopt
$ debuild
$ cd ..
$ sudo debi package_name*.changes

您需要检查软件包的构建脚本,并确保使用 “CFLAGS=-g -Wall” 来编译二进制文件。

12.4.3. 获取回溯

当您遇到程序崩溃时,提交带有剪切粘贴的回溯信息的错误报告是一个好主意。

可以通过以下步骤获得回溯。

  • gdb(1) 下运行程序。

  • 重现崩溃。

    • 这会导致您返回到 gdb 提示符。

  • gdb 提示符下键入 “bt”。

如果程序冻结,您可以按 Ctrl-C 在运行 gdb 的终端中崩溃程序,以获取 gdb 提示符。

[Tip] 提示

通常,您会看到一个回溯,其中顶部的一行或多行位于 “malloc()” 或 “g_malloc()” 中。 当发生这种情况时,您的回溯很可能不是很有用。 找到一些有用信息的最简单方法是将环境变量 “$MALLOC_CHECK_” 设置为值 2 (malloc(3))。 您可以在运行 gdb 时通过执行以下操作来完成此操作。

 $ MALLOC_CHECK_=2 gdb hello

12.4.4. 高级 gdb 命令

表 12.12. 高级 gdb 命令列表

命令 命令目标的描述
(gdb) thread apply all bt 获取多线程程序的所有线程的回溯
(gdb) bt full 获取函数调用堆栈上的参数
(gdb) thread apply all bt full 获取回溯和参数,作为前面选项的组合
(gdb) thread apply all bt full 10 获取回溯和参数,用于顶部 10 个调用以截断不相关的输出
(gdb) set logging on gdb 输出的日志写入文件(默认文件为 “gdb.txt”)

12.4.5. 调试 X 错误

如果 GNOME 程序 preview1 收到 X 错误,您应该会看到如下消息。

The program 'preview1' received an X Window System error.

如果是这种情况,您可以尝试使用 “--sync” 运行程序,并在 “gdk_x_error” 函数处中断,以便获取回溯。

12.4.6. 检查库的依赖关系

使用 ldd(1) 找出程序对库的依赖关系,如下所示。

$ 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)

为了使 ls(1) 在 `chroot` 环境中工作,上述库必须在您的 `chroot` 环境中可用。

请参阅 第 9.5.6 节,“追踪程序活动”

12.4.7. 内存泄漏检测工具

Debian 中有几种内存泄漏检测工具可用。

表 12.13. 内存泄漏检测工具列表


12.4.8. 静态代码分析工具

有类似 lint 的工具用于 静态代码分析

表 12.14. 静态代码分析工具列表

软件包 popcon 大小 描述
splint http://qa.debian.org/popcon.php?package=splint 1836 用于静态检查 C 程序中错误的工具
rats http://qa.debian.org/popcon.php?package=rats 876 用于安全性的粗略审计工具 (C, C++, PHP, Perl 和 Python 代码)
flawfinder http://qa.debian.org/popcon.php?package=flawfinder 188 用于检查 C/C++ 源代码并查找安全漏洞的工具
perl http://qa.debian.org/popcon.php?package=perl 17077 带有内部静态代码检查器的解释器:B::Lint(3perl)
pylint http://qa.debian.org/popcon.php?package=pylint 416 Python 代码静态检查器
jlint http://qa.debian.org/popcon.php?package=jlint 156 Java 程序检查器
weblint-perl http://qa.debian.org/popcon.php?package=weblint-perl 57 HTML 的语法和最小样式检查器
linklint http://qa.debian.org/popcon.php?package=linklint 432 快速链接检查器和网站维护工具
libxml2-utils http://qa.debian.org/popcon.php?package=libxml2-utils 139 带有 xmllint(1) 的实用程序,用于验证 XML 文件

12.4.9. 反汇编二进制文件

您可以使用 objdump(1) 反汇编二进制代码,如下所示。

$  objdump -m i386 -b binary -D /usr/lib/grub/x86_64-pc/stage1
[Note] 注意

gdb(1) 可用于交互式反汇编代码。

12.5. Flex — 更好的 Lex

Flex 是一个与 Lex 兼容的快速 词法分析器 生成器。

可以在 “info flex” 中找到 flex(1) 的教程。

您需要提供自己的 “main()” 和 “yywrap()”。 否则,您的 flex 程序应如下所示进行编译,而无需库。 这是因为 “yywrap” 是一个宏,并且 “%option main” 隐式地打开了 “%option noyywrap”。

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

或者,您可以在 cc(1) 命令行末尾使用 “-lfl” 链接器选项进行编译(如带有 “-ll” 的 AT&T-Lex)。 在这种情况下,不需要 “%option”。

12.6. Bison — 更好的 Yacc

一些软件包在 Debian 中提供了与 Yacc 兼容的前瞻 LR 解析器LALR 解析器 生成器。

表 12.15. Yacc 兼容 LALR 解析器生成器列表


bison(1) 的教程可以在 "info bison" 中找到。

你需要提供你自己的 "main()" 和 "yyerror()"。 "main()" 调用 "yyparse()",后者调用 "yylex()",通常用 Flex 创建。

%%

%%

12.7. Autoconf

Autoconf 是一个用于生成 shell 脚本的工具,这些脚本可以自动配置软件包的源代码,以适应许多类 Unix 系统,它使用了完整的 GNU 构建系统。

autoconf(1) 生成配置文件脚本 "configure"。 "configure" 使用 "Makefile.in" 模板自动创建自定义的 "Makefile"。

12.7.1. 编译和安装程序

[Warning] 警告

安装编译后的程序时,不要覆盖系统文件。

Debian 不会触及 "/usr/local/" 或 "/opt" 中的文件。 因此,如果你从源代码编译程序,请将其安装到 "/usr/local/" 中,这样它就不会干扰 Debian。

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

12.7.2. 卸载程序

如果你有原始源代码,并且它使用了 autoconf(1)/automake(1),并且你还记得你是如何配置它的,请按如下方式执行以卸载程序。

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

或者,如果你绝对确定安装过程仅将文件放在 "/usr/local/" 下,并且那里没有任何重要的东西,你可以通过以下方式删除其所有内容。

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

如果你不确定文件安装在哪里,你应该考虑使用 checkinstall 包中的 checkinstall(8),它为卸载提供了干净的路径。 它现在支持使用 "-D" 选项创建 Debian 软件包。

12.8. Perl 短脚本疯狂

虽然任何 AWK 脚本都可以使用 a2p(1) 自动重写为 Perl,但单行 AWK 脚本最好手动转换为单行 Perl 脚本。

让我们考虑以下 AWK 脚本代码片段。

awk '($2=="1957") { print $3 }' |

这等同于以下任何一行。

perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |
perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |
perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |
perl -lane 'print $F[2] if $F[1] eq "1957"' |
perl -lane 'print$F[2]if$F[1]eq+1957' |

最后一个是一个谜题。 它利用了以下 Perl 特性。

  • 空格是可选的。

  • 存在从数字到字符串的自动转换。

有关命令行选项,请参阅 perlrun(1)。 有关更多疯狂的 Perl 脚本,Perl Golf 可能会很有趣。

12.9. Web

基本交互式动态网页可以按如下方式制作。

  • 使用 HTML 表单向浏览器用户呈现查询。

  • 填写并单击表单条目会从浏览器向 Web 服务器发送以下 URL 字符串之一,其中包含编码的参数。

    • "http://www.foo.dom/cgi-bin/program.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/cgi-bin/program.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/program.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

  • URL 中的 "%nn" 将被替换为十六进制值为 nn 的字符。

  • 环境变量设置为: "QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3""。

  • Web 服务器上的 CGI 程序("program.*" 中的任何一个)使用环境变量 "$QUERY_STRING" 执行自身。

  • CGI 程序的 stdout 被发送到 Web 浏览器,并呈现为交互式动态网页。

出于安全原因,最好不要手工制作新的 hack 来解析 CGI 参数。 在 Perl 和 Python 中有成熟的模块用于处理它们。 PHP 自带这些功能。 当需要客户端数据存储时,使用 HTTP cookies。 当需要客户端数据处理时,经常使用 Javascript

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

在 Google 上搜索 "CGI tutorial",直接在浏览器地址中输入编码的 URL http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial,是查看 CGI 脚本在 Google 服务器上运行情况的好方法。

12.10. 源代码转换

有一些程序可以转换源代码。

表 12.16. 源代码转换工具列表

软件包 popcon 大小 关键词 描述
perl http://qa.debian.org/popcon.php?package=perl 17077 AWK→PERL 将源代码从 AWK 转换为 PERL: a2p(1)
f2c http://qa.debian.org/popcon.php?package=f2c 424 FORTRAN→C 将源代码从 FORTRAN 77 转换为 C/C++: f2c(1)
protoize http://qa.debian.org/popcon.php?package=protoize 125 ANSI C 从 C 代码创建/删除 ANSI 原型
intel2gas http://qa.debian.org/popcon.php?package=intel2gas 72 intel→gas 从 NASM (Intel 格式) 到 GNU 汇编器 (GAS) 的转换器

12.11. 制作 Debian 软件包

如果你想制作 Debian 软件包,请阅读以下内容。

有一些软件包可以帮助打包,例如 dh-make, dh-make-perl 等。