第 12 章 编程
这里我给出一些 Debian 系统中的信息,帮助学习编程的人找出打包的源代码。下面是值得关注的软件包和与之对应的文档。
表 12.1. 帮助编程的软件包清单
软件包 | 流行度 | 大小 | 包 |
---|---|---|---|
autoconf | V:41, I:282 | 1846 | 由 autoconf-doc 包提供的“info autoconf ” |
automake | V:42, I:279 | 1830 | 由 automake1.10-doc 包提供的“info automake ” |
bash | V:791, I:999 | 6469 | 由 bash-doc 包提供的“info bash ” |
bison | V:9, I:103 | 2815 | 由 bison-doc 包提供的“info bison ” |
cpp | V:319, I:770 | 42 | 由 cpp-doc 包提供的“info cpp ” |
ddd | V:0, I:10 | 4184 | 由 ddd-doc 包提供的“info ddd ” |
exuberant-ctags | V:5, I:37 | 341 | exuberant-ctags(1) |
flex | V:9, I:93 | 1279 | 由 flex-doc 包提供的“info flex ” |
gawk | V:368, I:454 | 2558 | 由 gawk-doc 包提供的“info gawk ” |
gcc | V:165, I:604 | 45 | 由 gcc-doc 包提供的“info gcc ” |
gdb | V:13, I:114 | 9789 | 由 gdb-doc 包提供的“info gdb ” |
gettext | V:48, I:312 | 5843 | 由 gettext-doc 包提供的“info gettext ” |
gfortran | V:11, I:98 | 16 | 由 gfortran-doc 包提供的“info gfortran ”(Fortran 95) |
fpc | I:3 | 121 | fpc(1) 和由 fp-docs 包提供的 html 文档(Pascal) |
glade | V:0, I:8 | 1730 | 通过 UI Builder 菜单提供的文档 |
libc6 | V:935, I:999 | 12771 | 通过 glibc-doc 和 glibc-doc-reference 提供的“info libc ” |
make | V:157, I:609 | 1592 | 通过 make-doc 包提供的“info make ” |
xutils-dev | V:1, I:12 | 1466 | imake(1),xmkmf(1) 等。 |
mawk | V:372, I:997 | 242 | mawk(1) |
perl | V:610, I:992 | 705 | perl(1) 以及通过 perl-doc 和 perl-doc-html 提供的 html 文档 |
python | V:293, I:923 | 68 | python(1) 以及通过 python-doc 包提供的 html 文档 |
tcl | V:31, I:414 | 22 | tcl(3) 以及通过 tcl-doc 包提供的更详细的手册页文档 |
tk | V:30, I:406 | 22 | tk(3) 以及通过 tk-doc 包提供的更详细的手册页文档 |
ruby | V:137, I:318 | 35 | ruby(1) 以及通过 ri 包提供的交互式参考手册 |
vim | V:106, I:398 | 3231 | 通过 vim-doc 包提供的帮助(F1)菜单 |
susv2 | I:0 | 16 | 通过“单一UNIX规范(版本2)”获取(英语文档) |
susv3 | I:0 | 16 | 通过“单一UNIX规范(版本3)”获取(英语文档) |
安装 manpages
和 manpages-dev
包之后,可以通过运行“man 名称
”查看手册页中的参考信息。安装了 GNU 工具的相关文档包之后,可以通过运行“info 程序名称
”查看参考文档。某些 GFDL 协议的文档与 DFSG 并不兼容,所以你可能需要在 main
仓库中包含 contrib
和 non-free
才能下载并安装它们。
警告 | |
---|---|
不要用“ |
小心 | |
---|---|
你可以把从源代码编译得到的程序直接放到“ |
提示 | |
---|---|
“歌曲:99瓶啤酒”的代码示例可以给你提供实践各种语言的好范本。 |
12.1. Shell 脚本
Shell 脚本 是指包含有下面格式的可执行的文本文件。
#!/bin/sh
……命令行
第一行指明了读取并执行这个文件的 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
”,它是某个实际 shell 程序的链接。
对
lenny
或更老的系统来说,它是 bash(1)对
squeeze
或更新的系统来说,它是 dash(1)
避免编写具有 bashisms(bash 化)或者 zshisms(zsh 化)语法的 shell 脚本,确保脚本在所有 POSIX shell 之间具有可移植性。你可以使用 checkbashisms(1) 对其进行检查。
表 12.2. 典型 bashism 语法列表
好的: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 和外部命令的不同,它的实现也有差别。
避免使用除“
-n
”以外的任何命令行选项。避免在字符串中使用转义序列,因为根据 shell 不同,计算后的结果也不一样。
注意 | |
---|---|
尽管“ |
提示 | |
---|---|
如果你想要在输出字符串中嵌入转义序列,用 “ |
12.1.2. Shell 参数
特殊的 shell 参数经常在 shell 脚本里面被用到。
表 12.3. shell 参数列表
shell 参数 | 值 |
---|---|
$0 | shell 或 shell 脚本的名称 |
$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 “ (出错退出) |
以上这些操作中 “:
“ 实际上都是可选的。
有 “
:
“ 等于测试的 var 值是存在且非空没有 “
:
“ 等于测试的 var 值只是存在的,可以为空
表 12.5. 重要的 shell 参数替换列表
参数替换形式 | 结果 |
---|---|
${var%suffix} | 删除位于 var 结尾的 suffix 最小匹配模式 |
${var%%suffix} | 删除位于 var 结尾的 suffix 最大匹配模式 |
${var#prefix} | 删除位于 var 开头的 prefix 最小匹配模式 |
${var##prefix} | 删除位于 var 开头的 prefix 最大匹配模式 |
12.1.3. Shell 条件语句
每个命令都会返回 退出状态,这可以被条件语句使用。
成功:0 (“True”)
失败:非0 (“False”)
注意 | |
---|---|
“0” 在 shell 条件语句中的意思是 “True”,然而 “0” 在 C 条件语句中的含义为 “False”。 |
注意 | |
---|---|
“ |
如下所示是需要记忆的基础 条件语法。
“
<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
”选项而被调用时不会在该行意外地退出。
表 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
“ 可以用来重新开始下一次循环。
提示 | |
---|---|
C 语言中的数值迭代可以用 seq(1) 实现来生成 “ |
提示 | |
---|---|
12.1.5. shell 命令行的处理顺序
shell 大致以下列的顺序来处理一个脚本。
shell 读取一行。
如果该行包含有
"…"
或'…'
,shell 对该行各部分进行分组作为 一个标识(one token) (译注:one token 是指 shell 识别的一个结构单元).shell 通过下列方式将行中的其它部分分隔进 标识(tokens)。
空白字符:
<空格> <tab> <换行符>
元字符:
< > | ; & ( )
shell 会检查每一个不位于
"…"
或'...'
的 token 中的 保留字 来调整它的行为。- 保留字:
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 将不位于
"…"
或'...'
中的 glob 路径 展开为匹配的文件名。*
→ 任何字符?
→ 一个字符[…]
→ 任何位于 “…
“ 中的字符
shell 从下列几方面查找 命令 并执行。
函数 定义
内建命令
“
$PATH
” 中的可执行文件
shell 前往下一行,并按照这个顺序从头再次进行处理。
双引号中的单引号是没有效果的。
在 shell 中执行 “set -x
” 或使用 “-x
” 选项启动 shell 可以让 shell 显示出所有执行的命令。这对调试来说是非常方便的。
12.1.6. 用于 shell 脚本的应用程序
为了使你的 shell 程序在 Debian 系统上尽可能地具有可移植性,你应该只使用 必要的 软件包所提供的应用程序。
“
aptitude search ~E
“,列出 必要的 软件包。“
dpkg -L <package_name> |grep '/man/man.*/'
“,列出<package_name>
软件包所提供的 man 手册。
表 12.8. 包含用于 shell 脚本的小型应用程序的软件包
软件包 | 流行度 | 大小 | 说明 |
---|---|---|---|
coreutils | V:891, I:999 | 17478 | GNU 核心工具 |
debianutils | V:925, I:999 | 230 | 用于 Debian 的各种工具 |
bsdmainutils | V:60, I:996 | 26 | 来自 FreeBSD 更多的工具集合 |
bsdutils | V:673, I:999 | 393 | 来自 4.4BSD-Lite 的基础工具 |
moreutils | V:11, I:35 | 237 | 额外的 Unix 工具 |
提示 | |
---|---|
尽管 |
12.1.7. shell 脚本对话框
一个简单的 shell 程序的用户界面中,echo
和 read
命令的交互性较为一般,你可以使用对话程序等来提升交互性。
表 12.9. 用户界面程序列表
软件包 | 流行度 | 大小 | 说明 |
---|---|---|---|
x11-utils | V:180, I:599 | 712 | xmessage(1):在一个窗口中显示一条消息或疑问(X) |
whiptail | V:87, I:995 | 71 | 从 shell 脚本中显示用户友好的对话框(newt) |
dialog | V:15, I:123 | 1222 | 从 shell 脚本中显示用户友好的对话框(ncurses) |
zenity | V:87, I:409 | 384 | 从 shell 脚本中显示图形对话框(gtk2.0) |
ssft | V:0, I:0 | 75 | Shell 脚本前端工具 (zenity, kdialog, and 带有 gettext 的 dialog 封装) |
gettext | V:48, I:312 | 5843 | “/usr/bin/gettext.sh ”:翻译信息 |
12.1.8. zenity 的 shell 脚本案例
下面是一个简单的脚本,它通过 dvdisaster(1) 创建了带有 RS02 补充数据的 ISO 映像。
#!/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
,自从上次目标文件被修改后,如果目标文件依赖的相关文件发生了改变,那么就会更新目标文件,或者目标文件不存在,那么这些文件更新可能会同时发生。
规则文件的语法如下所示。
目标:[相关文件 ...]
[TAB] 命令1
[TAB] -命令2 # 忽略错误
[TAB] @命令3 # 禁止回显
这里面的 “[TAB]
“ 是一个 TAB 代码。每一行在进行变量替换以后会被 shell 解释。在行末使用 “\
“ 来继续此脚本。使用 “$$
“ 输入 “$
“ 来获得 shell 脚本中的环境变量值。
目标跟相关文件也可以通过隐式规则给出,例如,如下所示。
%.o: %.c header.h
在这里,目标包含了 “%
“ 字符 (只是它们中确切的某一个)。”%
“ 字符能够匹配实际的目标文件中任意一个非空的子串。相关文件同样使用 “%
“ 来表明它们是怎样与目标文件建立联系的。
表 12.10. 自动变量的列表
自动变量 | 值 |
---|---|
$@ | 当前目标 |
$< | 首个相关文件 |
$? | 所有较新的相关文件 |
$^ | 所有相关文件 |
$ | 目标模式中,$ 指代匹配符 “% “ 匹配的部分 |
表 12.11. 变量扩展的列表
变量扩展 | 说明 |
---|---|
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 编程语言所使用的头文件和库例程。
参考信息如下。
“
info libc
”(C 库函数参考)gcc(1) 和 “
info gcc
”each_C_library_function_name(3)
Kernighan & Ritchie,“C 程序设计语言”,第二版(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
为了使用 sqrt(3),必须使用 “-lm
” 链接来自 libc6
软件包的库 “/usr/lib/libm.so
”。实际的库文件位于 “/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
gdb
的好的教程由 “info gdb
“ 提供或者可以在网上的其他地方找到。如下是用 gdb(1) 在”程序
“带有 “-g
“ 选项编译的时候来产生调试信息。
$ gdb program
(gdb) b 1 # 在第一行设置断点
(gdb) run args # 带参数运行程序
(gdb) next # 执行下一步
...
(gdb) step # 单步进入
...
(gdb) p parm # 打印 parm 的值
...
(gdb) p parm=12 # 把值设为 12
...
(gdb) quit
提示 | |
---|---|
许多 gdb(1) 命令都能被缩写。Tab 扩展跟在 shell 一样都能工作。 |
12.4.2. 调试 Debian 软件包
因为在 Debian 系统上默认所有已安装的二进制程序都是精简的,绝大多数的调试符号已经从常规的软件包中移除了。为了能用 gdb(1) 调试 Debian 软件包,相对应的 *-dbg
软件包或 *-dbgsym
软件包需要被安装 (例如 libc6
需要安装 libc6-dbg
,coreutils
需要安装 coreutils-dbgsym
)。
老式的软件包将提供相应的 *-dbg
软件包。它将和原始软件包一起,直接放在 Debian main 档案库。对于新的软件包,当它们编译时,将会自动产生 *-dbgsym
软件包,那些调试软件包将被独立放在 debian-debug 档案库. 更多信息请参阅 Debian Wiki 文档 .
如果一个需要被调试的软件包没有提供其 *-dbg
软件包或 *-dbgsym
软件包,你需要按如下所示的从源代码中重构并且安装它。
$ mkdir /path/new ; cd /path/new
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ apt-get source package_name
$ cd package_name*
$ sudo apt-get build-dep ./
按需修改 bug。
软件包调试版本跟它的官方 Debian 版本不冲突,例如当重新编译已存在的软件包版本产生的 “+debug1
“ 后缀,如下所示是编译未发行的软件包版本产生的 “~pre1
“ 后缀。
$ dch -i
如下所示编译并安装带有调试符号的软件包。
$ export DEB_BUILD_OPTIONS=nostrip noopt
$ debuild
$ cd ..
$ sudo debi package_name*.changes
你需要检查软件包的构建脚本并确保编译二进制的时候使用了 “CFLAGS=-g -Wall
“ 选项。
12.4.3. 获得栈帧
当你碰到程序崩溃的时候,报告 bug 时附上栈帧信息是个不错的注意。
如下所示的步骤就可以取得栈帧信息。
在 gdb(1) 中运行程序。
重现崩溃。
- 它使得你重新回到
gdb
提示符。
- 它使得你重新回到
在
gdb
提示符后输入 “bt
“。
程序在终端中的 gdb
环境运行时,如果它没反应,你可以按下 Ctrl-C
来中止程序来取得 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 错误,您应当看见一条下面这样的信息。
'preview1' 程序出现 X 桌面系统错误。
如果就是这种情况,你可以尝试在运行程序的时候加上 “--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`ed 环境,以上的库在 `chroot`ed 环境也必须是可用的。
12.4.7. 内存泄漏检测工具
Debian 上有一些可用的内存泄漏检测工具。
表 12.13. 内存泄漏检测工具的列表
软件包 | 流行度 | 大小 | 说明 |
---|---|---|---|
libc6-dev | V:249, I:620 | 14357 | mtrace(1):调试 glibc 中的 malloc |
valgrind | V:6, I:46 | 80378 | 内存调试器和分析器 |
electric-fence | V:0, I:5 | 70 | malloc(e) 调试器 |
leaktracer | V:0, I:3 | 57 | C++ 程序内存泄露跟踪器 |
libdmalloc5 | V:0, I:3 | 393 | 内存分配库调试 |
12.4.8. 静态代码分析工具
表 12.14. 静态代码分析工具的列表
软件包 | 流行度 | 大小 | 说明 |
---|---|---|---|
splint | V:0, I:4 | 2315 | 静态检查 C 程序 bug 的工具 |
flawfinder | V:0, I:0 | 181 | 检查 C/C++ 源代码和查找安全漏洞的工具 |
perl | V:610, I:992 | 705 | 带有内部静态代码检测的解释器:B::Lint(3perl) |
pylint | V:2, I:13 | 1371 | Python 代码静态检查器 |
weblint-perl | V:0, I:1 | 32 | 用于 HTML 的小巧的语法检查器 |
linklint | V:0, I:0 | 344 | 快速的网站维护工具及链接检查器 |
libxml2-utils | V:22, I:246 | 182 | 使用 xmllint(1) 来检查 XML 文件 |
12.4.9. 反汇编二进制程序
你可以使用下面的方式通过 objdump(1) 反编译二进制代码。
$ objdump -m i386 -b binary -D /usr/lib/grub/x86_64-pc/stage1
注意 | |
---|---|
gdb(1) 可以用来交互式反汇编代码。 |
12.5. 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 | V:9, I:103 | 2815 | GNU LALR 解析器生成器 |
byacc | V:0, I:6 | 160 | 伯克利(Berkeley)LALR 解析器生成器 |
btyacc | V:0, I:0 | 243 | 基于 byacc 的回溯解析生成器 |
可以使用 “info bison
” 查看 bison(1) 的教程。
你需要提供你自己的的 “main()
“ 和 “yyerror()
“.通常,Flex 创建的 “main()
“ 调用 “yyparse()
“,它又调用了 “yylex()
“.
%%
%%
12.7. Autoconf
autoconf 是一种用于自动生成软件源代码包配置 shell 脚本的工具,以适应使用完整 GNU 构建系统的各种类 Unix 系统。
autoconf(1) 生成配置脚本 “configure
”。“configure
” 使用 “Makefile.in
” 模板自动生成一个自定义的 “Makefile
”。
12.7.1. 编译并安装程序
警告 | |
---|---|
当你安装编译好的程序的时候,注意不要覆盖系统文件。 |
Debian 不会在 “/usr/local
“ 或 “/opt
“ 目录下创建文件。如果你想要源码编译程序,把它安装到 “/usr/local/
“ 目录下,因为这并不会影响到 Debian。
$ cd src
$ ./configure --prefix=/usr/local
$ make
$ make install # 这一步是把文件安装到系统上
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
“ 是使用一个 16 进制字符的nn
值代替。环境变量设置为: “
QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3"
“.Web服务器上的CGI程序 (任何一个 “
program.*
“)在执行时,都会使用”$QUERY_STRING
“环境变量.CGI 程序的
stdout
发送到浏览器,作为交互式的动态 web 页面展示。
出于安全考虑,最好不要自己从头编写解析CGI参数的手艺. 在Perl和Python中有现有的模块可以使用. PHP 中包含这些功能. 当需要客户端数据存储时, 可使用HTTP cookies . 当需要处理客户端数据时, 通常使用Javascript.
更多信息,参见 通用网关接口, Apache 软件基金会, 和 JavaScript.
直接在浏览器地址中输入 http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial 就可以在 Google 上搜索 “CGI tutorial”。这是在 Google 服务器上查看 CGI 脚本运行的好方法。
12.10. 源代码转换
源代码转换程序。
表 12.16. 源代码转换工具列表
软件包 | 流行度 | 大小 | 关键词 | 说明 |
---|---|---|---|---|
perl | V:610, I:992 | 705 | AWK→PERL | 把源代码从 AWK 转换为 PERL: a2p(1) |
f2c | V:0, I:6 | 442 | FORTRAN→C | 把源代码从 FORTRAN 77 转换成 C/C++: f2c(1) |
intel2gas | V:0, I:0 | 178 | intel→gas | 从 NASM (Intel 格式)转换成 GNU 汇编程序(GAS) |
12.11. 制作 Debian 包
如果你想制作一个 Debian 包,阅读下面内容。
第 2 章 Debian 软件包管理 理解基本的包管理系统
第 2.7.13 节 “移植一个软件包到 stable 系统” 理解基本的移植过程
第 9.10.4 节 “Chroot 系统” 理解基本的 chroot 技术
debuild(1), pbuilder(1) 和 pdebuild(1)
Debian 新维护者指引 作为一个教程(
maint-guide
包)Debian 开发者参考手册 (
developers-reference
包)Debian 策略手册 (
debian-policy
包)Debian 维护者指南 (
debmake-doc
包)
debmake
, dh-make
, dh-make-perl
等软件包,对软件包打包过程,也有帮助。